mabuseif commited on
Commit
93bdd7b
·
verified ·
1 Parent(s): b081a29

Update app/component_selection.py

Browse files
Files changed (1) hide show
  1. app/component_selection.py +24 -12
app/component_selection.py CHANGED
@@ -2,6 +2,7 @@
2
  HVAC Component Selection Module
3
  Provides UI for selecting building components in the HVAC Load Calculator.
4
  All dependencies are included within this file for standalone operation.
 
5
  """
6
 
7
  import streamlit as st
@@ -125,16 +126,22 @@ class Floor(BuildingComponent):
125
  floor_type: str = "Concrete"
126
  ground_contact: bool = True
127
  ground_temperature_c: float = 25.0
 
128
 
129
  def __post_init__(self):
130
  super().__post_init__()
131
  self.component_type = ComponentType.FLOOR
132
  self.orientation = Orientation.NOT_APPLICABLE
 
 
 
 
133
 
134
  def to_dict(self) -> dict:
135
  base_dict = super().to_dict()
136
  base_dict.update({
137
- "floor_type": self.floor_type, "ground_contact": self.ground_contact, "ground_temperature_c": self.ground_temperature_c
 
138
  })
139
  return base_dict
140
 
@@ -425,7 +432,7 @@ class ComponentSelectionInterface:
425
  roof_options = self.reference_data.data["roof_types"]
426
  selected_roof = st.selectbox("Roof Type", options=list(roof_options.keys()))
427
  u_value = st.number_input("U-Value (W/m²·K)", min_value=0.0, value=float(roof_options[selected_roof]["u_value"]), step=0.01)
428
- roof_group = st.selectbox("Roof Group (ASHRAE)", ["1", "2", "3", "4", "5", "6", "7", "8"], index=0)
429
  slope = st.selectbox("Slope", ["Flat", "Pitched"], index=0)
430
  absorptivity = st.selectbox("Solar Absorptivity", ["Light (0.3)", "Light to Medium (0.45)", "Medium (0.6)", "Medium to Dark (0.75)", "Dark (0.9)"], index=2)
431
 
@@ -453,18 +460,18 @@ class ComponentSelectionInterface:
453
  required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Roof Type", "Roof Group", "Slope", "Absorptivity"]
454
  # Provide template with valid roof_group values
455
  template_data = pd.DataFrame(columns=required_cols)
456
- template_data.loc[0] = ["Example Roof", 10.0, 0.3, "Horizontal", "Concrete Roof", "1", "Flat", 0.6]
457
  st.download_button(label="Download Roof Template", data=template_data.to_csv(index=False), file_name="roof_template.csv", mime="text/csv")
458
  if uploaded_file:
459
  df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
460
  if all(col in df.columns for col in required_cols):
461
- valid_roof_groups = {"1", "2", "3", "4", "5", "6", "7", "8"}
462
  for _, row in df.iterrows():
463
  try:
464
  roof_group = str(row["Roof Group"])
465
  if roof_group not in valid_roof_groups:
466
- st.warning(f"Invalid Roof Group '{roof_group}' in row '{row['Name']}'. Defaulting to '1'.")
467
- roof_group = "1"
468
  new_roof = Roof(
469
  name=str(row["Name"]), u_value=float(row["U-Value (W/m²·K)"]), area=float(row["Area (m²)"]),
470
  orientation=Orientation(row["Orientation"]), roof_type=str(row["Roof Type"]),
@@ -491,19 +498,21 @@ class ComponentSelectionInterface:
491
  with col1:
492
  name = st.text_input("Name", "New Floor")
493
  area = st.number_input("Area (m²)", min_value=0.0, value=1.0, step=0.1)
 
494
  with col2:
495
  floor_options = self.reference_data.data["floor_types"]
496
  selected_floor = st.selectbox("Floor Type", options=list(floor_options.keys()))
497
  u_value = st.number_input("U-Value (W/m²·K)", min_value=0.0, value=float(floor_options[selected_floor]["u_value"]), step=0.01)
498
  ground_contact = st.selectbox("Ground Contact", ["Yes", "No"], index=0 if floor_options[selected_floor]["ground_contact"] else 1)
499
- ground_temp = st.number_input("Ground Temperature (°C)", value=25.0, step=0.1) if ground_contact == "Yes" else 25.0
500
 
501
  submitted = st.form_submit_button("Add Floor")
502
  if submitted and not session_state.add_floor_submitted:
503
  try:
504
  new_floor = Floor(
505
  name=name, u_value=u_value, area=area, floor_type=selected_floor,
506
- ground_contact=(ground_contact == "Yes"), ground_temperature_c=ground_temp
 
507
  )
508
  self.component_library.add_component(new_floor)
509
  session_state.components['floors'].append(new_floor)
@@ -518,8 +527,10 @@ class ComponentSelectionInterface:
518
 
519
  elif method == "File Upload":
520
  uploaded_file = st.file_uploader("Upload Floors File", type=["csv", "xlsx"], key="floor_upload")
521
- required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Floor Type", "Ground Contact", "Ground Temperature (°C)"]
522
- st.download_button(label="Download Floor Template", data=pd.DataFrame(columns=required_cols).to_csv(index=False), file_name="floor_template.csv", mime="text/csv")
 
 
523
  if uploaded_file:
524
  df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
525
  if all(col in df.columns for col in required_cols):
@@ -528,7 +539,7 @@ class ComponentSelectionInterface:
528
  new_floor = Floor(
529
  name=str(row["Name"]), u_value=float(row["U-Value (W/m²·K)"]), area=float(row["Area (m²)"]),
530
  floor_type=str(row["Floor Type"]), ground_contact=(str(row["Ground Contact"]).lower() == "yes"),
531
- ground_temperature_c=float(row["Ground Temperature (°C)"])
532
  )
533
  self.component_library.add_component(new_floor)
534
  session_state.components['floors'].append(new_floor)
@@ -678,7 +689,7 @@ class ComponentSelectionInterface:
678
  headers = {
679
  ComponentType.WALL: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Wall Type", "Wall Group", "Absorptivity", "Shading Coefficient", "Infiltration Rate (CFM)", "Delete"],
680
  ComponentType.ROOF: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Roof Type", "Roof Group", "Slope", "Absorptivity", "Delete"],
681
- ComponentType.FLOOR: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Floor Type", "Ground Contact", "Ground Temperature (°C)", "Delete"],
682
  ComponentType.WINDOW: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "SHGC", "Shading Device", "Shading Coefficient", "Frame Type", "Frame Percentage", "Infiltration Rate (CFM)", "Delete"],
683
  ComponentType.DOOR: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Door Type", "Infiltration Rate (CFM)", "Delete"]
684
  }[component_type]
@@ -707,6 +718,7 @@ class ComponentSelectionInterface:
707
  cols[4].write(comp.floor_type)
708
  cols[5].write("Yes" if comp.ground_contact else "No")
709
  cols[6].write(comp.ground_temperature_c if comp.ground_contact else "N/A")
 
710
  elif component_type == ComponentType.WINDOW:
711
  cols[4].write(comp.shgc)
712
  cols[5].write(comp.shading_device)
 
2
  HVAC Component Selection Module
3
  Provides UI for selecting building components in the HVAC Load Calculator.
4
  All dependencies are included within this file for standalone operation.
5
+ Updated 2025-04-28: Added perimeter field to Floor class for F-factor calculations.
6
  """
7
 
8
  import streamlit as st
 
126
  floor_type: str = "Concrete"
127
  ground_contact: bool = True
128
  ground_temperature_c: float = 25.0
129
+ perimeter: float = 0.0 # NEW: Perimeter in meters for F-factor calculations
130
 
131
  def __post_init__(self):
132
  super().__post_init__()
133
  self.component_type = ComponentType.FLOOR
134
  self.orientation = Orientation.NOT_APPLICABLE
135
+ if self.perimeter < 0:
136
+ raise ValueError("Perimeter cannot be negative")
137
+ if self.ground_contact and not (-10 <= self.ground_temperature_c <= 40):
138
+ raise ValueError("Ground temperature must be between -10°C and 40°C for ground-contact floors")
139
 
140
  def to_dict(self) -> dict:
141
  base_dict = super().to_dict()
142
  base_dict.update({
143
+ "floor_type": self.floor_type, "ground_contact": self.ground_contact,
144
+ "ground_temperature_c": self.ground_temperature_c, "perimeter": self.perimeter
145
  })
146
  return base_dict
147
 
 
432
  roof_options = self.reference_data.data["roof_types"]
433
  selected_roof = st.selectbox("Roof Type", options=list(roof_options.keys()))
434
  u_value = st.number_input("U-Value (W/m²·K)", min_value=0.0, value=float(roof_options[selected_roof]["u_value"]), step=0.01)
435
+ roof_group = st.selectbox("Roof Group (ASHRAE)", ["A", "B", "C", "D", "E", "F", "G"], index=0) # Updated to letter-based groups
436
  slope = st.selectbox("Slope", ["Flat", "Pitched"], index=0)
437
  absorptivity = st.selectbox("Solar Absorptivity", ["Light (0.3)", "Light to Medium (0.45)", "Medium (0.6)", "Medium to Dark (0.75)", "Dark (0.9)"], index=2)
438
 
 
460
  required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Roof Type", "Roof Group", "Slope", "Absorptivity"]
461
  # Provide template with valid roof_group values
462
  template_data = pd.DataFrame(columns=required_cols)
463
+ template_data.loc[0] = ["Example Roof", 10.0, 0.3, "Horizontal", "Concrete Roof", "A", "Flat", 0.6]
464
  st.download_button(label="Download Roof Template", data=template_data.to_csv(index=False), file_name="roof_template.csv", mime="text/csv")
465
  if uploaded_file:
466
  df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
467
  if all(col in df.columns for col in required_cols):
468
+ valid_roof_groups = {"A", "B", "C", "D", "E", "F", "G"}
469
  for _, row in df.iterrows():
470
  try:
471
  roof_group = str(row["Roof Group"])
472
  if roof_group not in valid_roof_groups:
473
+ st.warning(f"Invalid Roof Group '{roof_group}' in row '{row['Name']}'. Defaulting to 'A'.")
474
+ roof_group = "A"
475
  new_roof = Roof(
476
  name=str(row["Name"]), u_value=float(row["U-Value (W/m²·K)"]), area=float(row["Area (m²)"]),
477
  orientation=Orientation(row["Orientation"]), roof_type=str(row["Roof Type"]),
 
498
  with col1:
499
  name = st.text_input("Name", "New Floor")
500
  area = st.number_input("Area (m²)", min_value=0.0, value=1.0, step=0.1)
501
+ perimeter = st.number_input("Perimeter (m)", min_value=0.0, value=0.0, step=0.1) # NEW: Perimeter input
502
  with col2:
503
  floor_options = self.reference_data.data["floor_types"]
504
  selected_floor = st.selectbox("Floor Type", options=list(floor_options.keys()))
505
  u_value = st.number_input("U-Value (W/m²·K)", min_value=0.0, value=float(floor_options[selected_floor]["u_value"]), step=0.01)
506
  ground_contact = st.selectbox("Ground Contact", ["Yes", "No"], index=0 if floor_options[selected_floor]["ground_contact"] else 1)
507
+ ground_temp = st.number_input("Ground Temperature (°C)", min_value=-10.0, max_value=40.0, value=25.0, step=0.1) if ground_contact == "Yes" else 25.0
508
 
509
  submitted = st.form_submit_button("Add Floor")
510
  if submitted and not session_state.add_floor_submitted:
511
  try:
512
  new_floor = Floor(
513
  name=name, u_value=u_value, area=area, floor_type=selected_floor,
514
+ ground_contact=(ground_contact == "Yes"), ground_temperature_c=ground_temp,
515
+ perimeter=perimeter # NEW: Pass perimeter
516
  )
517
  self.component_library.add_component(new_floor)
518
  session_state.components['floors'].append(new_floor)
 
527
 
528
  elif method == "File Upload":
529
  uploaded_file = st.file_uploader("Upload Floors File", type=["csv", "xlsx"], key="floor_upload")
530
+ required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Floor Type", "Ground Contact", "Ground Temperature (°C)", "Perimeter (m)"] # NEW: Added Perimeter
531
+ template_data = pd.DataFrame(columns=required_cols)
532
+ template_data.loc[0] = ["Example Floor", 10.0, 0.4, "Concrete Slab", "Yes", 25.0, 12.0] # NEW: Added perimeter in template
533
+ st.download_button(label="Download Floor Template", data=template_data.to_csv(index=False), file_name="floor_template.csv", mime="text/csv")
534
  if uploaded_file:
535
  df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
536
  if all(col in df.columns for col in required_cols):
 
539
  new_floor = Floor(
540
  name=str(row["Name"]), u_value=float(row["U-Value (W/m²·K)"]), area=float(row["Area (m²)"]),
541
  floor_type=str(row["Floor Type"]), ground_contact=(str(row["Ground Contact"]).lower() == "yes"),
542
+ ground_temperature_c=float(row["Ground Temperature (°C)"]), perimeter=float(row["Perimeter (m)"]) # NEW: Read perimeter
543
  )
544
  self.component_library.add_component(new_floor)
545
  session_state.components['floors'].append(new_floor)
 
689
  headers = {
690
  ComponentType.WALL: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Wall Type", "Wall Group", "Absorptivity", "Shading Coefficient", "Infiltration Rate (CFM)", "Delete"],
691
  ComponentType.ROOF: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Roof Type", "Roof Group", "Slope", "Absorptivity", "Delete"],
692
+ ComponentType.FLOOR: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Floor Type", "Ground Contact", "Ground Temperature (°C)", "Perimeter (m)", "Delete"], # NEW: Added Perimeter
693
  ComponentType.WINDOW: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "SHGC", "Shading Device", "Shading Coefficient", "Frame Type", "Frame Percentage", "Infiltration Rate (CFM)", "Delete"],
694
  ComponentType.DOOR: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Door Type", "Infiltration Rate (CFM)", "Delete"]
695
  }[component_type]
 
718
  cols[4].write(comp.floor_type)
719
  cols[5].write("Yes" if comp.ground_contact else "No")
720
  cols[6].write(comp.ground_temperature_c if comp.ground_contact else "N/A")
721
+ cols[7].write(comp.perimeter) # NEW: Display perimeter
722
  elif component_type == ComponentType.WINDOW:
723
  cols[4].write(comp.shgc)
724
  cols[5].write(comp.shading_device)