Spaces:
Sleeping
Sleeping
Update app/component_selection.py
Browse files- 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,
|
|
|
|
| 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)", ["
|
| 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", "
|
| 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 = {"
|
| 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 '
|
| 467 |
-
roof_group = "
|
| 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 |
-
|
|
|
|
|
|
|
| 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)
|