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

Update app/component_selection.py

Browse files
Files changed (1) hide show
  1. app/component_selection.py +19 -17
app/component_selection.py CHANGED
@@ -3,6 +3,7 @@ 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
@@ -66,7 +67,7 @@ class BuildingComponent:
66
  @dataclass
67
  class Wall(BuildingComponent):
68
  wall_type: str = "Brick"
69
- wall_group: str = "A" # Changed default to valid ASHRAE group
70
  absorptivity: float = 0.6
71
  shading_coefficient: float = 1.0
72
  infiltration_rate_cfm: float = 0.0
@@ -80,7 +81,6 @@ class Wall(BuildingComponent):
80
  raise ValueError("Shading coefficient must be between 0 and 1")
81
  if self.infiltration_rate_cfm < 0:
82
  raise ValueError("Infiltration rate cannot be negative")
83
- # Validate wall_group
84
  VALID_WALL_GROUPS = {"A", "B", "C", "D", "E", "F", "G", "H"}
85
  if self.wall_group not in VALID_WALL_GROUPS:
86
  st.warning(f"Invalid wall_group '{self.wall_group}' for wall '{self.name}'. Defaulting to 'A'.")
@@ -97,7 +97,7 @@ class Wall(BuildingComponent):
97
  @dataclass
98
  class Roof(BuildingComponent):
99
  roof_type: str = "Concrete"
100
- roof_group: str = "A" # Changed to letter-based ASHRAE group
101
  slope: str = "Flat"
102
  absorptivity: float = 0.6
103
 
@@ -108,7 +108,6 @@ class Roof(BuildingComponent):
108
  self.orientation = Orientation.HORIZONTAL
109
  if not 0 <= self.absorptivity <= 1:
110
  raise ValueError("Absorptivity must be between 0 and 1")
111
- # Validate roof_group
112
  VALID_ROOF_GROUPS = {"A", "B", "C", "D", "E", "F", "G"}
113
  if self.roof_group not in VALID_ROOF_GROUPS:
114
  st.warning(f"Invalid roof_group '{self.roof_group}' for roof '{self.name}'. Defaulting to 'A'.")
@@ -126,7 +125,8 @@ class Floor(BuildingComponent):
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__()
@@ -141,7 +141,8 @@ class Floor(BuildingComponent):
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
 
@@ -376,7 +377,6 @@ class ComponentSelectionInterface:
376
  elif method == "File Upload":
377
  uploaded_file = st.file_uploader("Upload Walls File", type=["csv", "xlsx"], key="wall_upload")
378
  required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Wall Type", "Wall Group", "Absorptivity", "Shading Coefficient", "Infiltration Rate (CFM)"]
379
- # Provide template with valid wall_group values
380
  template_data = pd.DataFrame(columns=required_cols)
381
  template_data.loc[0] = ["Example Wall", 10.0, 2.0, "North", "Brick Wall", "A", 0.6, 1.0, 0.0]
382
  st.download_button(label="Download Wall Template", data=template_data.to_csv(index=False), file_name="wall_template.csv", mime="text/csv")
@@ -411,7 +411,6 @@ class ComponentSelectionInterface:
411
  if "add_roof_submitted" not in session_state:
412
  session_state.add_roof_submitted = False
413
 
414
- # Shared Roof-Level Fields
415
  st.subheader("Roof System Ventilation")
416
  air_volume = st.number_input("Air Volume (m³)", min_value=0.0, value=session_state.roof_air_volume_m3, step=1.0, help="Total volume between roof and ceiling")
417
  vent_options = {f"{k} (ACH={v})": v for k, v in self.reference_data.data["roof_ventilation_methods"].items()}
@@ -432,7 +431,7 @@ class ComponentSelectionInterface:
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
 
@@ -458,7 +457,6 @@ class ComponentSelectionInterface:
458
  elif method == "File Upload":
459
  uploaded_file = st.file_uploader("Upload Roofs File", type=["csv", "xlsx"], key="roof_upload")
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")
@@ -498,13 +496,14 @@ class ComponentSelectionInterface:
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:
@@ -512,7 +511,7 @@ class ComponentSelectionInterface:
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,19 +526,21 @@ class ComponentSelectionInterface:
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):
537
  for _, row in df.iterrows():
538
  try:
 
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,7 +690,7 @@ class ComponentSelectionInterface:
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,7 +719,8 @@ class ComponentSelectionInterface:
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)
 
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
+ Updated 2025-04-29: Fixed Floors table headings, added insulated field for dynamic F-factor.
7
  """
8
 
9
  import streamlit as st
 
67
  @dataclass
68
  class Wall(BuildingComponent):
69
  wall_type: str = "Brick"
70
+ wall_group: str = "A" # ASHRAE group
71
  absorptivity: float = 0.6
72
  shading_coefficient: float = 1.0
73
  infiltration_rate_cfm: float = 0.0
 
81
  raise ValueError("Shading coefficient must be between 0 and 1")
82
  if self.infiltration_rate_cfm < 0:
83
  raise ValueError("Infiltration rate cannot be negative")
 
84
  VALID_WALL_GROUPS = {"A", "B", "C", "D", "E", "F", "G", "H"}
85
  if self.wall_group not in VALID_WALL_GROUPS:
86
  st.warning(f"Invalid wall_group '{self.wall_group}' for wall '{self.name}'. Defaulting to 'A'.")
 
97
  @dataclass
98
  class Roof(BuildingComponent):
99
  roof_type: str = "Concrete"
100
+ roof_group: str = "A" # ASHRAE group
101
  slope: str = "Flat"
102
  absorptivity: float = 0.6
103
 
 
108
  self.orientation = Orientation.HORIZONTAL
109
  if not 0 <= self.absorptivity <= 1:
110
  raise ValueError("Absorptivity must be between 0 and 1")
 
111
  VALID_ROOF_GROUPS = {"A", "B", "C", "D", "E", "F", "G"}
112
  if self.roof_group not in VALID_ROOF_GROUPS:
113
  st.warning(f"Invalid roof_group '{self.roof_group}' for roof '{self.name}'. Defaulting to 'A'.")
 
125
  floor_type: str = "Concrete"
126
  ground_contact: bool = True
127
  ground_temperature_c: float = 25.0
128
+ perimeter: float = 0.0
129
+ insulated: bool = False # NEW: For dynamic F-factor
130
 
131
  def __post_init__(self):
132
  super().__post_init__()
 
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
+ "insulated": self.insulated
146
  })
147
  return base_dict
148
 
 
377
  elif method == "File Upload":
378
  uploaded_file = st.file_uploader("Upload Walls File", type=["csv", "xlsx"], key="wall_upload")
379
  required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Wall Type", "Wall Group", "Absorptivity", "Shading Coefficient", "Infiltration Rate (CFM)"]
 
380
  template_data = pd.DataFrame(columns=required_cols)
381
  template_data.loc[0] = ["Example Wall", 10.0, 2.0, "North", "Brick Wall", "A", 0.6, 1.0, 0.0]
382
  st.download_button(label="Download Wall Template", data=template_data.to_csv(index=False), file_name="wall_template.csv", mime="text/csv")
 
411
  if "add_roof_submitted" not in session_state:
412
  session_state.add_roof_submitted = False
413
 
 
414
  st.subheader("Roof System Ventilation")
415
  air_volume = st.number_input("Air Volume (m³)", min_value=0.0, value=session_state.roof_air_volume_m3, step=1.0, help="Total volume between roof and ceiling")
416
  vent_options = {f"{k} (ACH={v})": v for k, v in self.reference_data.data["roof_ventilation_methods"].items()}
 
431
  roof_options = self.reference_data.data["roof_types"]
432
  selected_roof = st.selectbox("Roof Type", options=list(roof_options.keys()))
433
  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)
434
+ roof_group = st.selectbox("Roof Group (ASHRAE)", ["A", "B", "C", "D", "E", "F", "G"], index=0)
435
  slope = st.selectbox("Slope", ["Flat", "Pitched"], index=0)
436
  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)
437
 
 
457
  elif method == "File Upload":
458
  uploaded_file = st.file_uploader("Upload Roofs File", type=["csv", "xlsx"], key="roof_upload")
459
  required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Roof Type", "Roof Group", "Slope", "Absorptivity"]
 
460
  template_data = pd.DataFrame(columns=required_cols)
461
  template_data.loc[0] = ["Example Roof", 10.0, 0.3, "Horizontal", "Concrete Roof", "A", "Flat", 0.6]
462
  st.download_button(label="Download Roof Template", data=template_data.to_csv(index=False), file_name="roof_template.csv", mime="text/csv")
 
496
  with col1:
497
  name = st.text_input("Name", "New Floor")
498
  area = st.number_input("Area (m²)", min_value=0.0, value=1.0, step=0.1)
499
+ perimeter = st.number_input("Perimeter (m)", min_value=0.0, value=0.0, step=0.1)
500
  with col2:
501
  floor_options = self.reference_data.data["floor_types"]
502
  selected_floor = st.selectbox("Floor Type", options=list(floor_options.keys()))
503
  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)
504
  ground_contact = st.selectbox("Ground Contact", ["Yes", "No"], index=0 if floor_options[selected_floor]["ground_contact"] else 1)
505
  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
506
+ insulated = st.checkbox("Insulated Floor (e.g., R-10)", value=False) # NEW: Insulation option
507
 
508
  submitted = st.form_submit_button("Add Floor")
509
  if submitted and not session_state.add_floor_submitted:
 
511
  new_floor = Floor(
512
  name=name, u_value=u_value, area=area, floor_type=selected_floor,
513
  ground_contact=(ground_contact == "Yes"), ground_temperature_c=ground_temp,
514
+ perimeter=perimeter, insulated=insulated
515
  )
516
  self.component_library.add_component(new_floor)
517
  session_state.components['floors'].append(new_floor)
 
526
 
527
  elif method == "File Upload":
528
  uploaded_file = st.file_uploader("Upload Floors File", type=["csv", "xlsx"], key="floor_upload")
529
+ required_cols = ["Name", "Area (m²)", "U-Value (W/m²·K)", "Floor Type", "Ground Contact", "Ground Temperature (°C)", "Perimeter (m)", "Insulated"] # NEW: Added Insulated
530
  template_data = pd.DataFrame(columns=required_cols)
531
+ template_data.loc[0] = ["Example Floor", 10.0, 0.4, "Concrete Slab", "Yes", 25.0, 12.0, "No"]
532
  st.download_button(label="Download Floor Template", data=template_data.to_csv(index=False), file_name="floor_template.csv", mime="text/csv")
533
  if uploaded_file:
534
  df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
535
  if all(col in df.columns for col in required_cols):
536
  for _, row in df.iterrows():
537
  try:
538
+ insulated = str(row["Insulated"]).lower() in ["yes", "true", "1"]
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)"]),
543
+ insulated=insulated
544
  )
545
  self.component_library.add_component(new_floor)
546
  session_state.components['floors'].append(new_floor)
 
690
  headers = {
691
  ComponentType.WALL: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Wall Type", "Wall Group", "Absorptivity", "Shading Coefficient", "Infiltration Rate (CFM)", "Delete"],
692
  ComponentType.ROOF: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Roof Type", "Roof Group", "Slope", "Absorptivity", "Delete"],
693
+ ComponentType.FLOOR: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Floor Type", "Ground Contact", "Ground Temperature (°C)", "Perimeter (m)", "Insulated", "Delete"], # NEW: Added Insulated
694
  ComponentType.WINDOW: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "SHGC", "Shading Device", "Shading Coefficient", "Frame Type", "Frame Percentage", "Infiltration Rate (CFM)", "Delete"],
695
  ComponentType.DOOR: ["Name", "Area (m²)", "U-Value (W/m²·K)", "Orientation", "Door Type", "Infiltration Rate (CFM)", "Delete"]
696
  }[component_type]
 
719
  cols[4].write(comp.floor_type)
720
  cols[5].write("Yes" if comp.ground_contact else "No")
721
  cols[6].write(comp.ground_temperature_c if comp.ground_contact else "N/A")
722
+ cols[7].write(comp.perimeter)
723
+ cols[8].write("Yes" if comp.insulated else "No") # NEW: Display insulated
724
  elif component_type == ComponentType.WINDOW:
725
  cols[4].write(comp.shgc)
726
  cols[5].write(comp.shading_device)