mabuseif commited on
Commit
de66edb
·
verified ·
1 Parent(s): d56a18a

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +2 -91
app/main.py CHANGED
@@ -1,13 +1,5 @@
1
  """
2
  HVAC Calculator Code Documentation
3
-
4
- This module contains the main Streamlit application for the HVAC Calculator.
5
- It provides a comprehensive interface for calculating heating and cooling loads
6
- using ASHRAE methods.
7
-
8
- Author: Dr Majed Abuseif
9
- Date: March 2025
10
- Version: 1.0.0
11
  """
12
 
13
  import streamlit as st
@@ -24,20 +16,18 @@ from typing import Dict, List, Any, Optional, Tuple
24
 
25
  # Import application modules
26
  from app.building_info_form import BuildingInfoForm
27
- from app.component_selection import ComponentSelectionInterface
28
  from app.results_display import ResultsDisplay
29
  from app.data_validation import DataValidation
30
  from app.data_persistence import DataPersistence
31
  from app.data_export import DataExport
32
 
33
- # Import data modules
34
- from data.building_components import Wall, Roof, Floor, Window, Door, Orientation, ComponentType
35
  from data.reference_data import ReferenceData
36
  from data.climate_data import ClimateData
37
  from data.ashrae_tables import ASHRAETables
38
 
39
  # Import utility modules
40
- from utils.component_library import ComponentLibrary
41
  from utils.u_value_calculator import UValueCalculator
42
  from utils.shading_system import ShadingSystem
43
  from utils.area_calculation_system import AreaCalculationSystem
@@ -50,26 +40,8 @@ from utils.scenario_comparison import ScenarioComparisonVisualization
50
  from utils.psychrometric_visualization import PsychrometricVisualization
51
  from utils.time_based_visualization import TimeBasedVisualization
52
 
53
-
54
  class HVACCalculator:
55
- """
56
- Main HVAC Calculator application class.
57
-
58
- This class initializes the Streamlit application and manages the navigation
59
- between different sections of the calculator.
60
-
61
- Attributes:
62
- building_info_form (BuildingInfoForm): Building information input form
63
- component_selection (ComponentSelection): Component selection interface
64
- results_display (ResultsDisplay): Results display module
65
- data_validation (DataValidation): Data validation module
66
- data_persistence (DataPersistence): Data persistence module
67
- data_export (DataExport): Data export module
68
- """
69
-
70
  def __init__(self):
71
- """Initialize the HVAC Calculator application."""
72
- # Set page configuration
73
  st.set_page_config(
74
  page_title="HVAC Load Calculator",
75
  page_icon="🌡️",
@@ -77,7 +49,6 @@ class HVACCalculator:
77
  initial_sidebar_state="expanded"
78
  )
79
 
80
- # Initialize session state if not exists
81
  if 'page' not in st.session_state:
82
  st.session_state.page = 'Building Information'
83
 
@@ -112,7 +83,6 @@ class HVACCalculator:
112
  if 'climate_data' not in st.session_state:
113
  st.session_state.climate_data = {}
114
 
115
- # Initialize application modules
116
  self.building_info_form = BuildingInfoForm()
117
  self.component_selection = ComponentSelectionInterface()
118
  self.results_display = ResultsDisplay()
@@ -120,16 +90,12 @@ class HVACCalculator:
120
  self.data_persistence = DataPersistence()
121
  self.data_export = DataExport()
122
 
123
- # Set up the application layout
124
  self.setup_layout()
125
 
126
  def setup_layout(self):
127
- """Set up the application layout with sidebar navigation."""
128
- # Application title
129
  st.sidebar.title("HVAC Load Calculator")
130
  st.sidebar.markdown("---")
131
 
132
- # Navigation
133
  st.sidebar.subheader("Navigation")
134
  pages = [
135
  "Building Information",
@@ -142,14 +108,11 @@ class HVACCalculator:
142
 
143
  selected_page = st.sidebar.radio("Go to", pages, index=pages.index(st.session_state.page))
144
 
145
- # Update session state if page changed
146
  if selected_page != st.session_state.page:
147
  st.session_state.page = selected_page
148
 
149
- # Display the selected page
150
  self.display_page(st.session_state.page)
151
 
152
- # Footer
153
  st.sidebar.markdown("---")
154
  st.sidebar.info(
155
  "HVAC Load Calculator v1.0.0\n\n"
@@ -161,12 +124,6 @@ class HVACCalculator:
161
  )
162
 
163
  def display_page(self, page: str):
164
- """
165
- Display the selected page.
166
-
167
- Args:
168
- page (str): The page to display
169
- """
170
  if page == "Building Information":
171
  self.building_info_form.display_building_info_form(st.session_state)
172
  elif page == "Climate Data":
@@ -181,58 +138,45 @@ class HVACCalculator:
181
  self.data_export.display()
182
 
183
  def display_climate_data(self):
184
- """Display the climate data page."""
185
  climate_data = ClimateData()
186
  climate_data.display_climate_input(st.session_state)
187
 
188
- # Store climate data in session state if locations are added
189
  if climate_data.locations:
190
  st.session_state["climate_data"] = climate_data.locations
191
 
192
  def display_internal_loads(self):
193
- """Display the internal loads page."""
194
  st.title("Internal Loads")
195
 
196
- # Check if building components are available
197
  if not any(st.session_state.components.values()):
198
  st.warning("Please define building components first.")
199
  st.button("Go to Building Components", on_click=self.navigate_to, args=["Building Components"])
200
  return
201
 
202
- # Tabs for different internal load types
203
  tabs = st.tabs(["People", "Lighting", "Equipment"])
204
 
205
- # People tab
206
  with tabs[0]:
207
  self.display_people_loads()
208
 
209
- # Lighting tab
210
  with tabs[1]:
211
  self.display_lighting_loads()
212
 
213
- # Equipment tab
214
  with tabs[2]:
215
  self.display_equipment_loads()
216
 
217
- # Display summary of internal loads
218
  self.display_internal_loads_summary()
219
 
220
- # Navigation buttons
221
  col1, col2 = st.columns(2)
222
  with col1:
223
  st.button("Back to Building Components", on_click=self.navigate_to, args=["Building Components"])
224
  with col2:
225
- # Validate internal loads before proceeding
226
  if self.data_validation.validate_internal_loads():
227
  st.button("Continue to Calculation Results", on_click=self.navigate_to, args=["Calculation Results"])
228
  else:
229
  st.button("Continue to Calculation Results", disabled=True)
230
 
231
  def display_people_loads(self):
232
- """Display the people loads section."""
233
  st.subheader("People")
234
 
235
- # Form for adding people loads
236
  with st.form("people_load_form"):
237
  col1, col2 = st.columns(2)
238
 
@@ -255,7 +199,6 @@ class HVACCalculator:
255
  submitted = st.form_submit_button("Add People Load")
256
 
257
  if submitted:
258
- # Create people load
259
  people_load = {
260
  "id": f"people_{len(st.session_state.internal_loads['people'])}",
261
  "name": name,
@@ -265,11 +208,9 @@ class HVACCalculator:
265
  "hours_in_operation": hours_in_operation
266
  }
267
 
268
- # Add to session state
269
  st.session_state.internal_loads['people'].append(people_load)
270
  st.success(f"Added {name} with {num_people} people")
271
 
272
- # Display existing people loads
273
  if st.session_state.internal_loads['people']:
274
  st.subheader("Existing People Loads")
275
 
@@ -286,7 +227,6 @@ class HVACCalculator:
286
 
287
  df = pd.DataFrame(people_data)
288
 
289
- # Display table with edit and delete buttons
290
  for i, row in df.iterrows():
291
  col1, col2, col3, col4, col5, col6, col7 = st.columns([2, 1, 2, 2, 1, 1, 1])
292
 
@@ -312,10 +252,8 @@ class HVACCalculator:
312
  st.experimental_rerun()
313
 
314
  def display_lighting_loads(self):
315
- """Display the lighting loads section."""
316
  st.subheader("Lighting")
317
 
318
- # Form for adding lighting loads
319
  with st.form("lighting_load_form"):
320
  col1, col2 = st.columns(2)
321
 
@@ -335,7 +273,6 @@ class HVACCalculator:
335
  submitted = st.form_submit_button("Add Lighting Load")
336
 
337
  if submitted:
338
- # Create lighting load
339
  lighting_load = {
340
  "id": f"lighting_{len(st.session_state.internal_loads['lighting'])}",
341
  "name": name,
@@ -345,11 +282,9 @@ class HVACCalculator:
345
  "hours_in_operation": hours_in_operation
346
  }
347
 
348
- # Add to session state
349
  st.session_state.internal_loads['lighting'].append(lighting_load)
350
  st.success(f"Added {name} with {power} W")
351
 
352
- # Display existing lighting loads
353
  if st.session_state.internal_loads['lighting']:
354
  st.subheader("Existing Lighting Loads")
355
 
@@ -366,7 +301,6 @@ class HVACCalculator:
366
 
367
  df = pd.DataFrame(lighting_data)
368
 
369
- # Display table with edit and delete buttons
370
  for i, row in df.iterrows():
371
  col1, col2, col3, col4, col5, col6, col7 = st.columns([2, 1, 1, 2, 1, 1, 1])
372
 
@@ -392,10 +326,8 @@ class HVACCalculator:
392
  st.experimental_rerun()
393
 
394
  def display_equipment_loads(self):
395
- """Display the equipment loads section."""
396
  st.subheader("Equipment")
397
 
398
- # Form for adding equipment loads
399
  with st.form("equipment_load_form"):
400
  col1, col2 = st.columns(2)
401
 
@@ -417,7 +349,6 @@ class HVACCalculator:
417
  submitted = st.form_submit_button("Add Equipment Load")
418
 
419
  if submitted:
420
- # Create equipment load
421
  equipment_load = {
422
  "id": f"equipment_{len(st.session_state.internal_loads['equipment'])}",
423
  "name": name,
@@ -428,11 +359,9 @@ class HVACCalculator:
428
  "hours_in_operation": hours_in_operation
429
  }
430
 
431
- # Add to session state
432
  st.session_state.internal_loads['equipment'].append(equipment_load)
433
  st.success(f"Added {name} with {power} W")
434
 
435
- # Display existing equipment loads
436
  if st.session_state.internal_loads['equipment']:
437
  st.subheader("Existing Equipment Loads")
438
 
@@ -450,7 +379,6 @@ class HVACCalculator:
450
 
451
  df = pd.DataFrame(equipment_data)
452
 
453
- # Display table with edit and delete buttons
454
  for i, row in df.iterrows():
455
  col1, col2, col3, col4, col5, col6, col7, col8 = st.columns([2, 1, 1, 1, 2, 1, 1, 1])
456
 
@@ -478,20 +406,16 @@ class HVACCalculator:
478
  st.experimental_rerun()
479
 
480
  def display_internal_loads_summary(self):
481
- """Display a summary of all internal loads."""
482
  st.subheader("Internal Loads Summary")
483
 
484
- # Check if any internal loads exist
485
  if not any(st.session_state.internal_loads.values()):
486
  st.info("No internal loads defined yet.")
487
  return
488
 
489
- # Calculate total loads
490
  total_people = sum(load['num_people'] for load in st.session_state.internal_loads['people'])
491
  total_lighting_power = sum(load['power'] * load['usage_factor'] for load in st.session_state.internal_loads['lighting'])
492
  total_equipment_power = sum(load['power'] * load['usage_factor'] for load in st.session_state.internal_loads['equipment'])
493
 
494
- # Display summary
495
  col1, col2, col3 = st.columns(3)
496
 
497
  with col1:
@@ -503,31 +427,18 @@ class HVACCalculator:
503
  with col3:
504
  st.metric("Total Equipment Power", f"{total_equipment_power:.1f} W")
505
 
506
- # Display pie chart of internal loads
507
  if total_lighting_power > 0 or total_equipment_power > 0:
508
- # Estimate people load (assuming 100W per person)
509
  people_power = total_people * 100
510
-
511
- # Create pie chart
512
  fig = px.pie(
513
  values=[people_power, total_lighting_power, total_equipment_power],
514
  names=['People', 'Lighting', 'Equipment'],
515
  title='Internal Loads Distribution'
516
  )
517
-
518
  st.plotly_chart(fig, use_container_width=True)
519
 
520
  def navigate_to(self, page: str):
521
- """
522
- Navigate to the specified page.
523
-
524
- Args:
525
- page (str): The page to navigate to
526
- """
527
  st.session_state.page = page
528
  st.experimental_rerun()
529
 
530
-
531
  if __name__ == "__main__":
532
- # Create and run the HVAC Calculator application
533
  app = HVACCalculator()
 
1
  """
2
  HVAC Calculator Code Documentation
 
 
 
 
 
 
 
 
3
  """
4
 
5
  import streamlit as st
 
16
 
17
  # Import application modules
18
  from app.building_info_form import BuildingInfoForm
19
+ from app.hvac_component_selection import ComponentSelectionInterface, Orientation, ComponentType, Wall, Roof, Floor, Window, Door
20
  from app.results_display import ResultsDisplay
21
  from app.data_validation import DataValidation
22
  from app.data_persistence import DataPersistence
23
  from app.data_export import DataExport
24
 
25
+ # Import data modules (adjusted to avoid building_components)
 
26
  from data.reference_data import ReferenceData
27
  from data.climate_data import ClimateData
28
  from data.ashrae_tables import ASHRAETables
29
 
30
  # Import utility modules
 
31
  from utils.u_value_calculator import UValueCalculator
32
  from utils.shading_system import ShadingSystem
33
  from utils.area_calculation_system import AreaCalculationSystem
 
40
  from utils.psychrometric_visualization import PsychrometricVisualization
41
  from utils.time_based_visualization import TimeBasedVisualization
42
 
 
43
  class HVACCalculator:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  def __init__(self):
 
 
45
  st.set_page_config(
46
  page_title="HVAC Load Calculator",
47
  page_icon="🌡️",
 
49
  initial_sidebar_state="expanded"
50
  )
51
 
 
52
  if 'page' not in st.session_state:
53
  st.session_state.page = 'Building Information'
54
 
 
83
  if 'climate_data' not in st.session_state:
84
  st.session_state.climate_data = {}
85
 
 
86
  self.building_info_form = BuildingInfoForm()
87
  self.component_selection = ComponentSelectionInterface()
88
  self.results_display = ResultsDisplay()
 
90
  self.data_persistence = DataPersistence()
91
  self.data_export = DataExport()
92
 
 
93
  self.setup_layout()
94
 
95
  def setup_layout(self):
 
 
96
  st.sidebar.title("HVAC Load Calculator")
97
  st.sidebar.markdown("---")
98
 
 
99
  st.sidebar.subheader("Navigation")
100
  pages = [
101
  "Building Information",
 
108
 
109
  selected_page = st.sidebar.radio("Go to", pages, index=pages.index(st.session_state.page))
110
 
 
111
  if selected_page != st.session_state.page:
112
  st.session_state.page = selected_page
113
 
 
114
  self.display_page(st.session_state.page)
115
 
 
116
  st.sidebar.markdown("---")
117
  st.sidebar.info(
118
  "HVAC Load Calculator v1.0.0\n\n"
 
124
  )
125
 
126
  def display_page(self, page: str):
 
 
 
 
 
 
127
  if page == "Building Information":
128
  self.building_info_form.display_building_info_form(st.session_state)
129
  elif page == "Climate Data":
 
138
  self.data_export.display()
139
 
140
  def display_climate_data(self):
 
141
  climate_data = ClimateData()
142
  climate_data.display_climate_input(st.session_state)
143
 
 
144
  if climate_data.locations:
145
  st.session_state["climate_data"] = climate_data.locations
146
 
147
  def display_internal_loads(self):
 
148
  st.title("Internal Loads")
149
 
 
150
  if not any(st.session_state.components.values()):
151
  st.warning("Please define building components first.")
152
  st.button("Go to Building Components", on_click=self.navigate_to, args=["Building Components"])
153
  return
154
 
 
155
  tabs = st.tabs(["People", "Lighting", "Equipment"])
156
 
 
157
  with tabs[0]:
158
  self.display_people_loads()
159
 
 
160
  with tabs[1]:
161
  self.display_lighting_loads()
162
 
 
163
  with tabs[2]:
164
  self.display_equipment_loads()
165
 
 
166
  self.display_internal_loads_summary()
167
 
 
168
  col1, col2 = st.columns(2)
169
  with col1:
170
  st.button("Back to Building Components", on_click=self.navigate_to, args=["Building Components"])
171
  with col2:
 
172
  if self.data_validation.validate_internal_loads():
173
  st.button("Continue to Calculation Results", on_click=self.navigate_to, args=["Calculation Results"])
174
  else:
175
  st.button("Continue to Calculation Results", disabled=True)
176
 
177
  def display_people_loads(self):
 
178
  st.subheader("People")
179
 
 
180
  with st.form("people_load_form"):
181
  col1, col2 = st.columns(2)
182
 
 
199
  submitted = st.form_submit_button("Add People Load")
200
 
201
  if submitted:
 
202
  people_load = {
203
  "id": f"people_{len(st.session_state.internal_loads['people'])}",
204
  "name": name,
 
208
  "hours_in_operation": hours_in_operation
209
  }
210
 
 
211
  st.session_state.internal_loads['people'].append(people_load)
212
  st.success(f"Added {name} with {num_people} people")
213
 
 
214
  if st.session_state.internal_loads['people']:
215
  st.subheader("Existing People Loads")
216
 
 
227
 
228
  df = pd.DataFrame(people_data)
229
 
 
230
  for i, row in df.iterrows():
231
  col1, col2, col3, col4, col5, col6, col7 = st.columns([2, 1, 2, 2, 1, 1, 1])
232
 
 
252
  st.experimental_rerun()
253
 
254
  def display_lighting_loads(self):
 
255
  st.subheader("Lighting")
256
 
 
257
  with st.form("lighting_load_form"):
258
  col1, col2 = st.columns(2)
259
 
 
273
  submitted = st.form_submit_button("Add Lighting Load")
274
 
275
  if submitted:
 
276
  lighting_load = {
277
  "id": f"lighting_{len(st.session_state.internal_loads['lighting'])}",
278
  "name": name,
 
282
  "hours_in_operation": hours_in_operation
283
  }
284
 
 
285
  st.session_state.internal_loads['lighting'].append(lighting_load)
286
  st.success(f"Added {name} with {power} W")
287
 
 
288
  if st.session_state.internal_loads['lighting']:
289
  st.subheader("Existing Lighting Loads")
290
 
 
301
 
302
  df = pd.DataFrame(lighting_data)
303
 
 
304
  for i, row in df.iterrows():
305
  col1, col2, col3, col4, col5, col6, col7 = st.columns([2, 1, 1, 2, 1, 1, 1])
306
 
 
326
  st.experimental_rerun()
327
 
328
  def display_equipment_loads(self):
 
329
  st.subheader("Equipment")
330
 
 
331
  with st.form("equipment_load_form"):
332
  col1, col2 = st.columns(2)
333
 
 
349
  submitted = st.form_submit_button("Add Equipment Load")
350
 
351
  if submitted:
 
352
  equipment_load = {
353
  "id": f"equipment_{len(st.session_state.internal_loads['equipment'])}",
354
  "name": name,
 
359
  "hours_in_operation": hours_in_operation
360
  }
361
 
 
362
  st.session_state.internal_loads['equipment'].append(equipment_load)
363
  st.success(f"Added {name} with {power} W")
364
 
 
365
  if st.session_state.internal_loads['equipment']:
366
  st.subheader("Existing Equipment Loads")
367
 
 
379
 
380
  df = pd.DataFrame(equipment_data)
381
 
 
382
  for i, row in df.iterrows():
383
  col1, col2, col3, col4, col5, col6, col7, col8 = st.columns([2, 1, 1, 1, 2, 1, 1, 1])
384
 
 
406
  st.experimental_rerun()
407
 
408
  def display_internal_loads_summary(self):
 
409
  st.subheader("Internal Loads Summary")
410
 
 
411
  if not any(st.session_state.internal_loads.values()):
412
  st.info("No internal loads defined yet.")
413
  return
414
 
 
415
  total_people = sum(load['num_people'] for load in st.session_state.internal_loads['people'])
416
  total_lighting_power = sum(load['power'] * load['usage_factor'] for load in st.session_state.internal_loads['lighting'])
417
  total_equipment_power = sum(load['power'] * load['usage_factor'] for load in st.session_state.internal_loads['equipment'])
418
 
 
419
  col1, col2, col3 = st.columns(3)
420
 
421
  with col1:
 
427
  with col3:
428
  st.metric("Total Equipment Power", f"{total_equipment_power:.1f} W")
429
 
 
430
  if total_lighting_power > 0 or total_equipment_power > 0:
 
431
  people_power = total_people * 100
 
 
432
  fig = px.pie(
433
  values=[people_power, total_lighting_power, total_equipment_power],
434
  names=['People', 'Lighting', 'Equipment'],
435
  title='Internal Loads Distribution'
436
  )
 
437
  st.plotly_chart(fig, use_container_width=True)
438
 
439
  def navigate_to(self, page: str):
 
 
 
 
 
 
440
  st.session_state.page = page
441
  st.experimental_rerun()
442
 
 
443
  if __name__ == "__main__":
 
444
  app = HVACCalculator()