mabuseif commited on
Commit
5712989
·
verified ·
1 Parent(s): 5a88ecf

Update app/results_display.py

Browse files
Files changed (1) hide show
  1. app/results_display.py +269 -242
app/results_display.py CHANGED
@@ -94,84 +94,92 @@ class ResultsDisplay:
94
  with col1:
95
  st.write("### Cooling Load Results")
96
 
97
- # Display cooling load metrics
98
- cooling_metrics = [
99
- {"name": "Total Cooling Load", "value": results["cooling"]["total_load"], "unit": "kW"},
100
- {"name": "Sensible Cooling Load", "value": results["cooling"]["sensible_load"], "unit": "kW"},
101
- {"name": "Latent Cooling Load", "value": results["cooling"]["latent_load"], "unit": "kW"},
102
- {"name": "Cooling Load per Area", "value": results["cooling"]["load_per_area"], "unit": "W/m²"}
103
- ]
104
-
105
- for metric in cooling_metrics:
106
- st.metric(
107
- label=metric["name"],
108
- value=f"{metric['value']:.2f} {metric['unit']}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  )
110
-
111
- # Display cooling load pie chart
112
- cooling_breakdown = {
113
- "Walls": results["cooling"]["component_loads"]["walls"],
114
- "Roof": results["cooling"]["component_loads"]["roof"],
115
- "Windows": results["cooling"]["component_loads"]["windows"],
116
- "Doors": results["cooling"]["component_loads"]["doors"],
117
- "People": results["cooling"]["component_loads"]["people"],
118
- "Lighting": results["cooling"]["component_loads"]["lighting"],
119
- "Equipment": results["cooling"]["component_loads"]["equipment"],
120
- "Infiltration": results["cooling"]["component_loads"]["infiltration"],
121
- "Ventilation": results["cooling"]["component_loads"]["ventilation"]
122
- }
123
-
124
- fig = px.pie(
125
- values=list(cooling_breakdown.values()),
126
- names=list(cooling_breakdown.keys()),
127
- title="Cooling Load Breakdown",
128
- color_discrete_sequence=px.colors.qualitative.Pastel
129
- )
130
-
131
- fig.update_traces(textposition='inside', textinfo='percent+label')
132
- fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')
133
-
134
- st.plotly_chart(fig, use_container_width=True)
135
 
136
  with col2:
137
  st.write("### Heating Load Results")
138
 
139
- # Display heating load metrics
140
- heating_metrics = [
141
- {"name": "Total Heating Load", "value": results["heating"]["total_load"], "unit": "kW"},
142
- {"name": "Heating Load per Area", "value": results["heating"]["load_per_area"], "unit": "W/m²"},
143
- {"name": "Design Heat Loss", "value": results["heating"]["design_heat_loss"], "unit": "kW"},
144
- {"name": "Safety Factor", "value": results["heating"]["safety_factor"], "unit": "%"}
145
- ]
146
-
147
- for metric in heating_metrics:
148
- st.metric(
149
- label=metric["name"],
150
- value=f"{metric['value']:.2f} {metric['unit']}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  )
152
-
153
- # Display heating load pie chart
154
- heating_breakdown = {
155
- "Walls": results["heating"]["component_loads"]["walls"],
156
- "Roof": results["heating"]["component_loads"]["roof"],
157
- "Floor": results["heating"]["component_loads"]["floor"],
158
- "Windows": results["heating"]["component_loads"]["windows"],
159
- "Doors": results["heating"]["component_loads"]["doors"],
160
- "Infiltration": results["heating"]["component_loads"]["infiltration"],
161
- "Ventilation": results["heating"]["component_loads"]["ventilation"]
162
- }
163
-
164
- fig = px.pie(
165
- values=list(heating_breakdown.values()),
166
- names=list(heating_breakdown.keys()),
167
- title="Heating Load Breakdown",
168
- color_discrete_sequence=px.colors.qualitative.Pastel
169
- )
170
-
171
- fig.update_traces(textposition='inside', textinfo='percent+label')
172
- fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')
173
-
174
- st.plotly_chart(fig, use_container_width=True)
175
 
176
  # Display tabular results
177
  st.subheader("Detailed Results")
@@ -180,168 +188,174 @@ class ResultsDisplay:
180
  tab1, tab2 = st.tabs(["Cooling Load Details", "Heating Load Details"])
181
 
182
  with tab1:
183
- # Create cooling load details table
184
- cooling_details = []
185
-
186
- # Add envelope components
187
- for wall in results["cooling"]["detailed_loads"]["walls"]:
188
- cooling_details.append({
189
- "Component Type": "Wall",
190
- "Name": wall["name"],
191
- "Orientation": wall["orientation"],
192
- "Area (m²)": wall["area"],
193
- "U-Value (W/m²·K)": wall["u_value"],
194
- "CLTD (°C)": wall["cltd"],
195
- "Load (kW)": wall["load"]
196
- })
197
-
198
- for roof in results["cooling"]["detailed_loads"]["roofs"]:
199
- cooling_details.append({
200
- "Component Type": "Roof",
201
- "Name": roof["name"],
202
- "Orientation": roof["orientation"],
203
- "Area (m²)": roof["area"],
204
- "U-Value (W/m²·K)": roof["u_value"],
205
- "CLTD (°C)": roof["cltd"],
206
- "Load (kW)": roof["load"]
207
- })
208
-
209
- for window in results["cooling"]["detailed_loads"]["windows"]:
210
- cooling_details.append({
211
- "Component Type": "Window",
212
- "Name": window["name"],
213
- "Orientation": window["orientation"],
214
- "Area (m²)": window["area"],
215
- "U-Value (W/m²·K)": window["u_value"],
216
- "SHGC": window["shgc"],
217
- "SCL (W/m²)": window["scl"],
218
- "Load (kW)": window["load"]
219
- })
220
-
221
- for door in results["cooling"]["detailed_loads"]["doors"]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  cooling_details.append({
223
- "Component Type": "Door",
224
- "Name": door["name"],
225
- "Orientation": door["orientation"],
226
- "Area ()": door["area"],
227
- "U-Value (W/m²·K)": door["u_value"],
228
- "CLTD (°C)": door["cltd"],
229
- "Load (kW)": door["load"]
230
  })
231
-
232
- # Add internal loads
233
- for internal_load in results["cooling"]["detailed_loads"]["internal"]:
234
  cooling_details.append({
235
- "Component Type": internal_load["type"],
236
- "Name": internal_load["name"],
237
- "Quantity": internal_load["quantity"],
238
- "Heat Gain (W)": internal_load["heat_gain"],
239
- "CLF": internal_load["clf"],
240
- "Load (kW)": internal_load["load"]
241
  })
242
-
243
- # Add infiltration and ventilation
244
- cooling_details.append({
245
- "Component Type": "Infiltration",
246
- "Name": "Air Infiltration",
247
- "Air Flow (m³/s)": results["cooling"]["detailed_loads"]["infiltration"]["air_flow"],
248
- "Sensible Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["sensible_load"],
249
- "Latent Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["latent_load"],
250
- "Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["total_load"]
251
- })
252
-
253
- cooling_details.append({
254
- "Component Type": "Ventilation",
255
- "Name": "Fresh Air",
256
- "Air Flow (m³/s)": results["cooling"]["detailed_loads"]["ventilation"]["air_flow"],
257
- "Sensible Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["sensible_load"],
258
- "Latent Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["latent_load"],
259
- "Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["total_load"]
260
- })
261
-
262
- # Display cooling details table
263
- cooling_df = pd.DataFrame(cooling_details)
264
- st.dataframe(cooling_df, use_container_width=True)
265
 
266
  with tab2:
267
- # Create heating load details table
268
- heating_details = []
269
-
270
- # Add envelope components
271
- for wall in results["heating"]["detailed_loads"]["walls"]:
272
- heating_details.append({
273
- "Component Type": "Wall",
274
- "Name": wall["name"],
275
- "Orientation": wall["orientation"],
276
- "Area (m²)": wall["area"],
277
- "U-Value (W/m²·K)": wall["u_value"],
278
- "Temperature Difference (°C)": wall["delta_t"],
279
- "Load (kW)": wall["load"]
280
- })
281
-
282
- for roof in results["heating"]["detailed_loads"]["roofs"]:
283
- heating_details.append({
284
- "Component Type": "Roof",
285
- "Name": roof["name"],
286
- "Orientation": roof["orientation"],
287
- "Area (m²)": roof["area"],
288
- "U-Value (W/m²·K)": roof["u_value"],
289
- "Temperature Difference (°C)": roof["delta_t"],
290
- "Load (kW)": roof["load"]
291
- })
292
-
293
- for floor in results["heating"]["detailed_loads"]["floors"]:
294
- heating_details.append({
295
- "Component Type": "Floor",
296
- "Name": floor["name"],
297
- "Area (m²)": floor["area"],
298
- "U-Value (W/m²·K)": floor["u_value"],
299
- "Temperature Difference (°C)": floor["delta_t"],
300
- "Load (kW)": floor["load"]
301
- })
302
-
303
- for window in results["heating"]["detailed_loads"]["windows"]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  heating_details.append({
305
- "Component Type": "Window",
306
- "Name": window["name"],
307
- "Orientation": window["orientation"],
308
- "Area ()": window["area"],
309
- "U-Value (W/m²·K)": window["u_value"],
310
- "Temperature Difference (°C)": wall["delta_t"],
311
- "Load (kW)": wall["load"]
312
  })
313
-
314
- for door in results["heating"]["detailed_loads"]["doors"]:
315
  heating_details.append({
316
- "Component Type": "Door",
317
- "Name": door["name"],
318
- "Orientation": door["orientation"],
319
- "Area ()": door["area"],
320
- "U-Value (W/m²·K)": door["u_value"],
321
- "Temperature Difference (°C)": door["delta_t"],
322
- "Load (kW)": door["load"]
323
  })
324
-
325
- # Add infiltration and ventilation
326
- heating_details.append({
327
- "Component Type": "Infiltration",
328
- "Name": "Air Infiltration",
329
- "Air Flow (m³/s)": results["heating"]["detailed_loads"]["infiltration"]["air_flow"],
330
- "Temperature Difference (°C)": results["heating"]["detailed_loads"]["infiltration"]["delta_t"],
331
- "Load (kW)": results["heating"]["detailed_loads"]["infiltration"]["load"]
332
- })
333
-
334
- heating_details.append({
335
- "Component Type": "Ventilation",
336
- "Name": "Fresh Air",
337
- "Air Flow (m³/s)": results["heating"]["detailed_loads"]["ventilation"]["air_flow"],
338
- "Temperature Difference (°C)": results["heating"]["detailed_loads"]["ventilation"]["delta_t"],
339
- "Load (kW)": results["heating"]["detailed_loads"]["ventilation"]["load"]
340
- })
341
-
342
- # Display heating details table
343
- heating_df = pd.DataFrame(heating_details)
344
- st.dataframe(heating_df, use_container_width=True)
345
 
346
  # Add download buttons for results
347
  st.subheader("Download Results")
@@ -349,39 +363,44 @@ class ResultsDisplay:
349
  col1, col2 = st.columns(2)
350
 
351
  with col1:
352
- if st.button("Download Cooling Load Results (CSV)"):
353
- cooling_csv = cooling_df.to_csv(index=False)
354
- st.download_button(
355
- label="Download CSV",
356
- data=cooling_csv,
357
- file_name=f"cooling_load_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
358
- mime="text/csv"
359
- )
 
360
 
361
  with col2:
362
- if st.button("Download Heating Load Results (CSV)"):
363
- heating_csv = heating_df.to_csv(index=False)
364
- st.download_button(
365
- label="Download CSV",
366
- data=heating_csv,
367
- file_name=f"heating_load_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
368
- mime="text/csv"
369
- )
 
370
 
371
  # Add button to download full report
372
  if st.button("Generate Full Report (Excel)"):
373
- # This would be implemented with the export functionality
374
  st.info("Excel report generation will be implemented in the Export module.")
375
 
376
  def _display_component_breakdown(self, session_state: Dict[str, Any]) -> None:
377
  """
378
- Display component breakdown visualizationolden visualization.
379
 
380
  Args:
381
  session_state: Streamlit session state containing calculation results
382
  """
383
  st.subheader("Component Breakdown")
384
 
 
 
 
 
385
  # Use component visualization module
386
  self.component_visualization.display_component_breakdown(
387
  session_state["calculation_results"],
@@ -397,6 +416,10 @@ class ResultsDisplay:
397
  """
398
  st.subheader("Psychrometric Analysis")
399
 
 
 
 
 
400
  # Use psychrometric visualization module
401
  self.psychrometric_visualization.display_psychrometric_chart(
402
  session_state["calculation_results"],
@@ -412,6 +435,10 @@ class ResultsDisplay:
412
  """
413
  st.subheader("Time Analysis")
414
 
 
 
 
 
415
  # Use time-based visualization module
416
  self.time_based_visualization.display_time_analysis(
417
  session_state["calculation_results"]
 
94
  with col1:
95
  st.write("### Cooling Load Results")
96
 
97
+ # Check if cooling results are available
98
+ if not results.get("cooling") or "total_load" not in results["cooling"]:
99
+ st.warning("Cooling load results are not available. Please check calculation inputs and try again.")
100
+ else:
101
+ # Display cooling load metrics
102
+ cooling_metrics = [
103
+ {"name": "Total Cooling Load", "value": results["cooling"]["total_load"], "unit": "kW"},
104
+ {"name": "Sensible Cooling Load", "value": results["cooling"]["sensible_load"], "unit": "kW"},
105
+ {"name": "Latent Cooling Load", "value": results["cooling"]["latent_load"], "unit": "kW"},
106
+ {"name": "Cooling Load per Area", "value": results["cooling"]["load_per_area"], "unit": "W/m²"}
107
+ ]
108
+
109
+ for metric in cooling_metrics:
110
+ st.metric(
111
+ label=metric["name"],
112
+ value=f"{metric['value']:.2f} {metric['unit']}"
113
+ )
114
+
115
+ # Display cooling load pie chart
116
+ cooling_breakdown = {
117
+ "Walls": results["cooling"]["component_loads"]["walls"],
118
+ "Roof": results["cooling"]["component_loads"]["roof"],
119
+ "Windows": results["cooling"]["component_loads"]["windows"],
120
+ "Doors": results["cooling"]["component_loads"]["doors"],
121
+ "People": results["cooling"]["component_loads"]["people"],
122
+ "Lighting": results["cooling"]["component_loads"]["lighting"],
123
+ "Equipment": results["cooling"]["component_loads"]["equipment"],
124
+ "Infiltration": results["cooling"]["component_loads"]["infiltration"],
125
+ "Ventilation": results["cooling"]["component_loads"]["ventilation"]
126
+ }
127
+
128
+ fig = px.pie(
129
+ values=list(cooling_breakdown.values()),
130
+ names=list(cooling_breakdown.keys()),
131
+ title="Cooling Load Breakdown",
132
+ color_discrete_sequence=px.colors.qualitative.Pastel
133
  )
134
+
135
+ fig.update_traces(textposition='inside', textinfo='percent+label')
136
+ fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')
137
+
138
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
  with col2:
141
  st.write("### Heating Load Results")
142
 
143
+ # Check if heating results are available
144
+ if not results.get("heating") or "total_load" not in results["heating"]:
145
+ st.warning("Heating load results are not available. Please check calculation inputs and try again.")
146
+ else:
147
+ # Display heating load metrics
148
+ heating_metrics = [
149
+ {"name": "Total Heating Load", "value": results["heating"]["total_load"], "unit": "kW"},
150
+ {"name": "Heating Load per Area", "value": results["heating"]["load_per_area"], "unit": "W/m²"},
151
+ {"name": "Design Heat Loss", "value": results["heating"]["design_heat_loss"], "unit": "kW"},
152
+ {"name": "Safety Factor", "value": results["heating"]["safety_factor"], "unit": "%"}
153
+ ]
154
+
155
+ for metric in heating_metrics:
156
+ st.metric(
157
+ label=metric["name"],
158
+ value=f"{metric['value']:.2f} {metric['unit']}"
159
+ )
160
+
161
+ # Display heating load pie chart
162
+ heating_breakdown = {
163
+ "Walls": results["heating"]["component_loads"]["walls"],
164
+ "Roof": results["heating"]["component_loads"]["roof"],
165
+ "Floor": results["heating"]["component_loads"]["floor"],
166
+ "Windows": results["heating"]["component_loads"]["windows"],
167
+ "Doors": results["heating"]["component_loads"]["doors"],
168
+ "Infiltration": results["heating"]["component_loads"]["infiltration"],
169
+ "Ventilation": results["heating"]["component_loads"]["ventilation"]
170
+ }
171
+
172
+ fig = px.pie(
173
+ values=list(heating_breakdown.values()),
174
+ names=list(heating_breakdown.keys()),
175
+ title="Heating Load Breakdown",
176
+ color_discrete_sequence=px.colors.qualitative.Pastel
177
  )
178
+
179
+ fig.update_traces(textposition='inside', textinfo='percent+label')
180
+ fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')
181
+
182
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
  # Display tabular results
185
  st.subheader("Detailed Results")
 
188
  tab1, tab2 = st.tabs(["Cooling Load Details", "Heating Load Details"])
189
 
190
  with tab1:
191
+ if not results.get("cooling") or "detailed_loads" not in results["cooling"]:
192
+ st.warning("Cooling load details are not available.")
193
+ else:
194
+ # Create cooling load details table
195
+ cooling_details = []
196
+
197
+ # Add envelope components
198
+ for wall in results["cooling"]["detailed_loads"]["walls"]:
199
+ cooling_details.append({
200
+ "Component Type": "Wall",
201
+ "Name": wall["name"],
202
+ "Orientation": wall["orientation"],
203
+ "Area ()": wall["area"],
204
+ "U-Value (W/m²·K)": wall["u_value"],
205
+ "CLTD (°C)": wall["cltd"],
206
+ "Load (kW)": wall["load"]
207
+ })
208
+
209
+ for roof in results["cooling"]["detailed_loads"]["roofs"]:
210
+ cooling_details.append({
211
+ "Component Type": "Roof",
212
+ "Name": roof["name"],
213
+ "Orientation": roof["orientation"],
214
+ "Area ()": roof["area"],
215
+ "U-Value (W/m²·K)": roof["u_value"],
216
+ "CLTD (°C)": roof["cltd"],
217
+ "Load (kW)": roof["load"]
218
+ })
219
+
220
+ for window in results["cooling"]["detailed_loads"]["windows"]:
221
+ cooling_details.append({
222
+ "Component Type": "Window",
223
+ "Name": window["name"],
224
+ "Orientation": window["orientation"],
225
+ "Area (m²)": window["area"],
226
+ "U-Value (W/m²·K)": window["u_value"],
227
+ "SHGC": window["shgc"],
228
+ "SCL (W/m²)": window["scl"],
229
+ "Load (kW)": window["load"]
230
+ })
231
+
232
+ for door in results["cooling"]["detailed_loads"]["doors"]:
233
+ cooling_details.append({
234
+ "Component Type": "Door",
235
+ "Name": door["name"],
236
+ "Orientation": door["orientation"],
237
+ "Area (m²)": door["area"],
238
+ "U-Value (W/m²·K)": door["u_value"],
239
+ "CLTD (°C)": door["cltd"],
240
+ "Load (kW)": door["load"]
241
+ })
242
+
243
+ # Add internal loads
244
+ for internal_load in results["cooling"]["detailed_loads"]["internal"]:
245
+ cooling_details.append({
246
+ "Component Type": internal_load["type"],
247
+ "Name": internal_load["name"],
248
+ "Quantity": internal_load["quantity"],
249
+ "Heat Gain (W)": internal_load["heat_gain"],
250
+ "CLF": internal_load["clf"],
251
+ "Load (kW)": internal_load["load"]
252
+ })
253
+
254
+ # Add infiltration and ventilation
255
  cooling_details.append({
256
+ "Component Type": "Infiltration",
257
+ "Name": "Air Infiltration",
258
+ "Air Flow (m³/s)": results["cooling"]["detailed_loads"]["infiltration"]["air_flow"],
259
+ "Sensible Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["sensible_load"],
260
+ "Latent Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["latent_load"],
261
+ "Load (kW)": results["cooling"]["detailed_loads"]["infiltration"]["total_load"]
 
262
  })
263
+
 
 
264
  cooling_details.append({
265
+ "Component Type": "Ventilation",
266
+ "Name": "Fresh Air",
267
+ "Air Flow (m³/s)": results["cooling"]["detailed_loads"]["ventilation"]["air_flow"],
268
+ "Sensible Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["sensible_load"],
269
+ "Latent Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["latent_load"],
270
+ "Load (kW)": results["cooling"]["detailed_loads"]["ventilation"]["total_load"]
271
  })
272
+
273
+ # Display cooling details table
274
+ cooling_df = pd.DataFrame(cooling_details)
275
+ st.dataframe(cooling_df, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
  with tab2:
278
+ if not results.get("heating") or "detailed_loads" not in results["heating"]:
279
+ st.warning("Heating load details are not available.")
280
+ else:
281
+ # Create heating load details table
282
+ heating_details = []
283
+
284
+ # Add envelope components
285
+ for wall in results["heating"]["detailed_loads"]["walls"]:
286
+ heating_details.append({
287
+ "Component Type": "Wall",
288
+ "Name": wall["name"],
289
+ "Orientation": wall["orientation"],
290
+ "Area ()": wall["area"],
291
+ "U-Value (W/m²·K)": wall["u_value"],
292
+ "Temperature Difference (°C)": wall["delta_t"],
293
+ "Load (kW)": wall["load"]
294
+ })
295
+
296
+ for roof in results["heating"]["detailed_loads"]["roofs"]:
297
+ heating_details.append({
298
+ "Component Type": "Roof",
299
+ "Name": roof["name"],
300
+ "Orientation": roof["orientation"],
301
+ "Area ()": roof["area"],
302
+ "U-Value (W/m²·K)": roof["u_value"],
303
+ "Temperature Difference (°C)": roof["delta_t"],
304
+ "Load (kW)": roof["load"]
305
+ })
306
+
307
+ for floor in results["heating"]["detailed_loads"]["floors"]:
308
+ heating_details.append({
309
+ "Component Type": "Floor",
310
+ "Name": floor["name"],
311
+ "Area ()": floor["area"],
312
+ "U-Value (W/m²·K)": floor["u_value"],
313
+ "Temperature Difference (°C)": floor["delta_t"],
314
+ "Load (kW)": floor["load"]
315
+ })
316
+
317
+ for window in results["heating"]["detailed_loads"]["windows"]:
318
+ heating_details.append({
319
+ "Component Type": "Window",
320
+ "Name": window["name"],
321
+ "Orientation": window["orientation"],
322
+ "Area (m²)": window["area"],
323
+ "U-Value (W/m²·K)": window["u_value"],
324
+ "Temperature Difference (°C)": window["delta_t"],
325
+ "Load (kW)": window["load"]
326
+ })
327
+
328
+ for door in results["heating"]["detailed_loads"]["doors"]:
329
+ heating_details.append({
330
+ "Component Type": "Door",
331
+ "Name": door["name"],
332
+ "Orientation": door["orientation"],
333
+ "Area (m²)": door["area"],
334
+ "U-Value (W/m²·K)": door["u_value"],
335
+ "Temperature Difference (°C)": door["delta_t"],
336
+ "Load (kW)": door["load"]
337
+ })
338
+
339
+ # Add infiltration and ventilation
340
  heating_details.append({
341
+ "Component Type": "Infiltration",
342
+ "Name": "Air Infiltration",
343
+ "Air Flow (m³/s)": results["heating"]["detailed_loads"]["infiltration"]["air_flow"],
344
+ "Temperature Difference (°C)": results["heating"]["detailed_loads"]["infiltration"]["delta_t"],
345
+ "Load (kW)": results["heating"]["detailed_loads"]["infiltration"]["load"]
 
 
346
  })
347
+
 
348
  heating_details.append({
349
+ "Component Type": "Ventilation",
350
+ "Name": "Fresh Air",
351
+ "Air Flow (m³/s)": results["heating"]["detailed_loads"]["ventilation"]["air_flow"],
352
+ "Temperature Difference (°C)": results["heating"]["detailed_loads"]["ventilation"]["delta_t"],
353
+ "Load (kW)": results["heating"]["detailed_loads"]["ventilation"]["load"]
 
 
354
  })
355
+
356
+ # Display heating details table
357
+ heating_df = pd.DataFrame(heating_details)
358
+ st.dataframe(heating_df, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
  # Add download buttons for results
361
  st.subheader("Download Results")
 
363
  col1, col2 = st.columns(2)
364
 
365
  with col1:
366
+ if results.get("cooling") and "detailed_loads" in results["cooling"]:
367
+ if st.button("Download Cooling Load Results (CSV)"):
368
+ cooling_csv = cooling_df.to_csv(index=False)
369
+ st.download_button(
370
+ label="Download CSV",
371
+ data=cooling_csv,
372
+ file_name=f"cooling_load_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
373
+ mime="text/csv"
374
+ )
375
 
376
  with col2:
377
+ if results.get("heating") and "detailed_loads" in results["heating"]:
378
+ if st.button("Download Heating Load Results (CSV)"):
379
+ heating_csv = heating_df.to_csv(index=False)
380
+ st.download_button(
381
+ label="Download CSV",
382
+ data=heating_csv,
383
+ file_name=f"heating_load_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
384
+ mime="text/csv"
385
+ )
386
 
387
  # Add button to download full report
388
  if st.button("Generate Full Report (Excel)"):
 
389
  st.info("Excel report generation will be implemented in the Export module.")
390
 
391
  def _display_component_breakdown(self, session_state: Dict[str, Any]) -> None:
392
  """
393
+ Display component breakdown visualization.
394
 
395
  Args:
396
  session_state: Streamlit session state containing calculation results
397
  """
398
  st.subheader("Component Breakdown")
399
 
400
+ if not session_state["calculation_results"].get("cooling") and not session_state["calculation_results"].get("heating"):
401
+ st.warning("No component breakdown data available.")
402
+ return
403
+
404
  # Use component visualization module
405
  self.component_visualization.display_component_breakdown(
406
  session_state["calculation_results"],
 
416
  """
417
  st.subheader("Psychrometric Analysis")
418
 
419
+ if not session_state["calculation_results"].get("cooling"):
420
+ st.warning("Psychrometric analysis requires cooling load results.")
421
+ return
422
+
423
  # Use psychrometric visualization module
424
  self.psychrometric_visualization.display_psychrometric_chart(
425
  session_state["calculation_results"],
 
435
  """
436
  st.subheader("Time Analysis")
437
 
438
+ if not session_state["calculation_results"].get("cooling"):
439
+ st.warning("Time analysis requires cooling load results.")
440
+ return
441
+
442
  # Use time-based visualization module
443
  self.time_based_visualization.display_time_analysis(
444
  session_state["calculation_results"]