Download app/component_selection.py from mabuseif/test-29: direct link, hf CLI and curl.
- Browser
- Download file 56.3 kB
-
https://huggingface.co/spaces/mabuseif/test-29/resolve/main/app/component_selection.py
- Command line
-
hf download hf://spaces/mabuseif/test-29/app/component_selection.py
-
curl -L -o component_selection.py https://huggingface.co/spaces/mabuseif/test-29/resolve/main/app/component_selection.py
56.3 kB
| """ | |
| Component selection interface for HVAC Load Calculator. | |
| This module provides the UI components for selecting building components. | |
| """ | |
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| from typing import Dict, List, Any, Optional, Tuple | |
| import json | |
| import os | |
| import uuid | |
| # Import data models | |
| from data.building_components import Wall, Roof, Floor, Window, Door, Orientation, ComponentType | |
| from utils.component_library import ComponentLibrary | |
| from utils.u_value_calculator import UValueCalculator | |
| class ComponentSelection: | |
| """Class for component selection interface.""" | |
| def __init__(self): | |
| """Initialize component selection interface.""" | |
| self.component_library = ComponentLibrary() | |
| self.u_value_calculator = UValueCalculator() | |
| def display(self) -> None: | |
| """Display component selection interface in Streamlit.""" | |
| # Check for pending actions in session state and process them | |
| self._process_pending_actions(st.session_state) | |
| self.display_component_selection(st.session_state) | |
| def _process_pending_actions(self, session_state: Dict[str, Any]) -> None: | |
| """ | |
| Process any pending actions stored in session state. | |
| Args: | |
| session_state: Streamlit session state | |
| """ | |
| # Ensure components structure exists | |
| if "components" not in session_state: | |
| session_state["components"] = { | |
| 'walls': [], | |
| 'roofs': [], | |
| 'floors': [], | |
| 'windows': [], | |
| 'doors': [] | |
| } | |
| # Initialize pending actions if not exists | |
| if "pending_actions" not in session_state: | |
| session_state["pending_actions"] = {} | |
| # Ensure all required pending action keys exist | |
| required_keys = [ | |
| "add_wall", "delete_wall", | |
| "add_roof", "delete_roof", | |
| "add_floor", "delete_floor", | |
| "add_window", "delete_window", | |
| "add_door", "delete_door" | |
| ] | |
| for key in required_keys: | |
| if key not in session_state["pending_actions"]: | |
| session_state["pending_actions"][key] = None | |
| # Process add wall action | |
| if session_state["pending_actions"].get("add_wall") is not None: | |
| new_wall = session_state["pending_actions"]["add_wall"] | |
| session_state["components"]["walls"].append(new_wall) | |
| session_state["pending_actions"]["add_wall"] = None | |
| # Process delete wall action | |
| if session_state["pending_actions"].get("delete_wall") is not None: | |
| wall_index = session_state["pending_actions"]["delete_wall"] | |
| if 0 <= wall_index < len(session_state["components"]["walls"]): | |
| session_state["components"]["walls"].pop(wall_index) | |
| session_state["pending_actions"]["delete_wall"] = None | |
| # Process add roof action | |
| if session_state["pending_actions"].get("add_roof") is not None: | |
| new_roof = session_state["pending_actions"]["add_roof"] | |
| session_state["components"]["roofs"].append(new_roof) | |
| session_state["pending_actions"]["add_roof"] = None | |
| # Process delete roof action | |
| if session_state["pending_actions"].get("delete_roof") is not None: | |
| roof_index = session_state["pending_actions"]["delete_roof"] | |
| if 0 <= roof_index < len(session_state["components"]["roofs"]): | |
| session_state["components"]["roofs"].pop(roof_index) | |
| session_state["pending_actions"]["delete_roof"] = None | |
| # Process add floor action | |
| if session_state["pending_actions"].get("add_floor") is not None: | |
| new_floor = session_state["pending_actions"]["add_floor"] | |
| session_state["components"]["floors"].append(new_floor) | |
| session_state["pending_actions"]["add_floor"] = None | |
| # Process delete floor action | |
| if session_state["pending_actions"].get("delete_floor") is not None: | |
| floor_index = session_state["pending_actions"]["delete_floor"] | |
| if 0 <= floor_index < len(session_state["components"]["floors"]): | |
| session_state["components"]["floors"].pop(floor_index) | |
| session_state["pending_actions"]["delete_floor"] = None | |
| # Process add window action | |
| if session_state["pending_actions"].get("add_window") is not None: | |
| new_window = session_state["pending_actions"]["add_window"] | |
| session_state["components"]["windows"].append(new_window) | |
| session_state["pending_actions"]["add_window"] = None | |
| # Process delete window action | |
| if session_state["pending_actions"].get("delete_window") is not None: | |
| window_index = session_state["pending_actions"]["delete_window"] | |
| if 0 <= window_index < len(session_state["components"]["windows"]): | |
| session_state["components"]["windows"].pop(window_index) | |
| session_state["pending_actions"]["delete_window"] = None | |
| # Process add door action | |
| if session_state["pending_actions"].get("add_door") is not None: | |
| new_door = session_state["pending_actions"]["add_door"] | |
| session_state["components"]["doors"].append(new_door) | |
| session_state["pending_actions"]["add_door"] = None | |
| # Process delete door action | |
| if session_state["pending_actions"].get("delete_door") is not None: | |
| door_index = session_state["pending_actions"]["delete_door"] | |
| if 0 <= door_index < len(session_state["components"]["doors"]): | |
| session_state["components"]["doors"].pop(door_index) | |
| session_state["pending_actions"]["delete_door"] = None | |
| def display_component_selection(self, session_state: Dict[str, Any]) -> None: | |
| """ | |
| Display component selection interface in Streamlit. | |
| Args: | |
| session_state: Streamlit session state for storing form data | |
| """ | |
| st.header("Building Components") | |
| # Initialize components in session state if not exists | |
| if "components" not in session_state: | |
| session_state["components"] = { | |
| "walls": [], | |
| "roofs": [], | |
| "floors": [], | |
| "windows": [], | |
| "doors": [] | |
| } | |
| # Initialize form submission flags if not exists | |
| if "form_submitted" not in session_state: | |
| session_state["form_submitted"] = { | |
| "wall_form": False, | |
| "roof_form": False, | |
| "floor_form": False, | |
| "window_form": False, | |
| "door_form": False | |
| } | |
| # Create tabs for different component types | |
| tab1, tab2, tab3, tab4, tab5 = st.tabs([ | |
| "Walls", | |
| "Roofs", | |
| "Floors", | |
| "Windows", | |
| "Doors" | |
| ]) | |
| with tab1: | |
| self._display_wall_selection(session_state) | |
| with tab2: | |
| self._display_roof_selection(session_state) | |
| with tab3: | |
| self._display_floor_selection(session_state) | |
| with tab4: | |
| self._display_window_selection(session_state) | |
| with tab5: | |
| self._display_door_selection(session_state) | |
| def _display_wall_selection(self, session_state: Dict[str, Any]) -> None: | |
| """ | |
| Display wall selection interface. | |
| Args: | |
| session_state: Streamlit session state for storing form data | |
| """ | |
| st.subheader("Walls") | |
| # Display existing walls | |
| if session_state["components"]["walls"]: | |
| st.write("Existing Walls:") | |
| # Create a table of existing walls | |
| wall_data = [] | |
| for i, wall in enumerate(session_state["components"]["walls"]): | |
| wall_data.append({ | |
| "ID": i + 1, | |
| "Name": wall.name, | |
| "Type": wall.wall_type, | |
| "U-Value (W/m²K)": f"{wall.u_value:.3f}", | |
| "Area (m²)": f"{wall.area:.2f}", | |
| "Orientation": wall.orientation.value | |
| }) | |
| wall_df = pd.DataFrame(wall_data) | |
| st.dataframe(wall_df) | |
| # Add edit and delete buttons for each wall | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| wall_to_edit = st.selectbox( | |
| "Select wall to edit:", | |
| options=range(1, len(session_state["components"]["walls"]) + 1), | |
| format_func=lambda x: f"Wall #{x}: {session_state['components']['walls'][x-1].name}" | |
| ) | |
| with col2: | |
| edit_col, delete_col = st.columns(2) | |
| with edit_col: | |
| if st.button("Edit Wall", key="edit_wall"): | |
| # Set the wall to edit in session state | |
| session_state["wall_to_edit"] = wall_to_edit - 1 | |
| with delete_col: | |
| if st.button("Delete Wall", key="delete_wall"): | |
| # Queue the delete action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["delete_wall"] = wall_to_edit - 1 | |
| # Add new wall form | |
| st.write("Add New Wall:") | |
| # Check if we're editing an existing wall | |
| editing_wall = "wall_to_edit" in session_state | |
| wall_to_edit = session_state.get("wall_to_edit", None) | |
| # Get the wall to edit if we're editing | |
| wall = None | |
| if editing_wall and wall_to_edit is not None: | |
| wall = session_state["components"]["walls"][wall_to_edit] | |
| # Wall form | |
| with st.form(key="wall_form"): | |
| # Wall name | |
| wall_name = st.text_input( | |
| "Wall Name:", | |
| value=wall.name if wall else "" | |
| ) | |
| # Wall type selection | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Get preset wall types | |
| preset_walls = self.component_library.get_preset_components_by_type(ComponentType.WALL) | |
| preset_wall_names = [wall.name for wall in preset_walls] | |
| wall_type = st.selectbox( | |
| "Wall Type:", | |
| options=["Custom"] + preset_wall_names, | |
| index=preset_wall_names.index(wall.wall_type) + 1 if wall and wall.wall_type in preset_wall_names else 0 | |
| ) | |
| # U-value | |
| with col2: | |
| if wall_type == "Custom": | |
| u_value = st.number_input( | |
| "U-Value (W/m²K):", | |
| min_value=0.0, | |
| max_value=10.0, | |
| value=wall.u_value if wall else 0.5, | |
| step=0.01 | |
| ) | |
| else: | |
| # Get U-value from preset wall | |
| selected_wall = next((w for w in preset_walls if w.name == wall_type), None) | |
| u_value = selected_wall.u_value if selected_wall else 0.5 | |
| st.write(f"U-Value: {u_value:.3f} W/m²K") | |
| # Wall dimensions | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| wall_height = st.number_input( | |
| "Wall Height (m):", | |
| min_value=0.1, | |
| max_value=50.0, | |
| value=3.0 if not wall else (wall.area / 10.0), # Assuming width of 10m if editing | |
| step=0.1 | |
| ) | |
| with col2: | |
| wall_width = st.number_input( | |
| "Wall Width (m):", | |
| min_value=0.1, | |
| max_value=100.0, | |
| value=10.0 if not wall else (wall.area / wall_height), | |
| step=0.1 | |
| ) | |
| # Calculate wall area | |
| wall_area = wall_height * wall_width | |
| st.write(f"Wall Area: {wall_area:.2f} m²") | |
| # Wall orientation | |
| orientation_options = [ | |
| Orientation.NORTH, | |
| Orientation.NORTHEAST, | |
| Orientation.EAST, | |
| Orientation.SOUTHEAST, | |
| Orientation.SOUTH, | |
| Orientation.SOUTHWEST, | |
| Orientation.WEST, | |
| Orientation.NORTHWEST | |
| ] | |
| orientation = st.selectbox( | |
| "Orientation:", | |
| options=orientation_options, | |
| index=orientation_options.index(wall.orientation) if wall else 0, | |
| format_func=lambda x: x.value | |
| ) | |
| # Wall color | |
| color_options = ["Light", "Medium", "Dark"] | |
| color = st.selectbox( | |
| "Color:", | |
| options=color_options, | |
| index=color_options.index(wall.color) if wall and wall.color in color_options else 1 | |
| ) | |
| # Submit button | |
| submit_label = "Update Wall" if editing_wall else "Add Wall" | |
| submit = st.form_submit_button(submit_label) | |
| if submit: | |
| # Create or update wall | |
| if wall_type == "Custom": | |
| # Create custom wall | |
| new_wall = Wall( | |
| id=f"custom_wall_{str(uuid.uuid4())[:8]}", | |
| name=wall_name, | |
| component_type=ComponentType.WALL, | |
| u_value=u_value, | |
| area=wall_area, | |
| orientation=orientation, | |
| color=color, | |
| wall_type="Custom", | |
| wall_group="Custom" | |
| ) | |
| else: | |
| # Clone preset wall and update properties | |
| selected_wall = next((w for w in preset_walls if w.name == wall_type), None) | |
| if selected_wall: | |
| new_wall = Wall( | |
| id=f"custom_wall_{str(uuid.uuid4())[:8]}", | |
| name=wall_name, | |
| component_type=ComponentType.WALL, | |
| u_value=selected_wall.u_value, | |
| area=wall_area, | |
| orientation=orientation, | |
| color=color, | |
| wall_type=selected_wall.wall_type, | |
| wall_group=selected_wall.wall_group, | |
| material_layers=selected_wall.material_layers | |
| ) | |
| # Add or update wall in session state | |
| if editing_wall: | |
| session_state["components"]["walls"][wall_to_edit] = new_wall | |
| del session_state["wall_to_edit"] | |
| else: | |
| # Queue the add action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["add_wall"] = new_wall | |
| def _display_roof_selection(self, session_state: Dict[str, Any]) -> None: | |
| """ | |
| Display roof selection interface. | |
| Args: | |
| session_state: Streamlit session state for storing form data | |
| """ | |
| st.subheader("Roofs") | |
| # Display existing roofs | |
| if session_state["components"]["roofs"]: | |
| st.write("Existing Roofs:") | |
| # Create a table of existing roofs | |
| roof_data = [] | |
| for i, roof in enumerate(session_state["components"]["roofs"]): | |
| roof_data.append({ | |
| "ID": i + 1, | |
| "Name": roof.name, | |
| "Type": roof.roof_type, | |
| "U-Value (W/m²K)": f"{roof.u_value:.3f}", | |
| "Area (m²)": f"{roof.area:.2f}", | |
| "Pitch (°)": f"{roof.pitch:.1f}" | |
| }) | |
| roof_df = pd.DataFrame(roof_data) | |
| st.dataframe(roof_df) | |
| # Add edit and delete buttons for each roof | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| roof_to_edit = st.selectbox( | |
| "Select roof to edit:", | |
| options=range(1, len(session_state["components"]["roofs"]) + 1), | |
| format_func=lambda x: f"Roof #{x}: {session_state['components']['roofs'][x-1].name}" | |
| ) | |
| with col2: | |
| edit_col, delete_col = st.columns(2) | |
| with edit_col: | |
| if st.button("Edit Roof", key="edit_roof"): | |
| # Set the roof to edit in session state | |
| session_state["roof_to_edit"] = roof_to_edit - 1 | |
| with delete_col: | |
| if st.button("Delete Roof", key="delete_roof"): | |
| # Queue the delete action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["delete_roof"] = roof_to_edit - 1 | |
| # Add new roof form | |
| st.write("Add New Roof:") | |
| # Check if we're editing an existing roof | |
| editing_roof = "roof_to_edit" in session_state | |
| roof_to_edit = session_state.get("roof_to_edit", None) | |
| # Get the roof to edit if we're editing | |
| roof = None | |
| if editing_roof and roof_to_edit is not None: | |
| roof = session_state["components"]["roofs"][roof_to_edit] | |
| # Roof form | |
| with st.form(key="roof_form"): | |
| # Roof name | |
| roof_name = st.text_input( | |
| "Roof Name:", | |
| value=roof.name if roof else "" | |
| ) | |
| # Roof type selection | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Get preset roof types | |
| preset_roofs = self.component_library.get_preset_components_by_type(ComponentType.ROOF) | |
| preset_roof_names = [roof.name for roof in preset_roofs] | |
| roof_type = st.selectbox( | |
| "Roof Type:", | |
| options=["Custom"] + preset_roof_names, | |
| index=preset_roof_names.index(roof.roof_type) + 1 if roof and roof.roof_type in preset_roof_names else 0 | |
| ) | |
| # U-value | |
| with col2: | |
| if roof_type == "Custom": | |
| u_value = st.number_input( | |
| "U-Value (W/m²K):", | |
| min_value=0.0, | |
| max_value=10.0, | |
| value=roof.u_value if roof else 0.25, | |
| step=0.01 | |
| ) | |
| else: | |
| # Get U-value from preset roof | |
| selected_roof = next((r for r in preset_roofs if r.name == roof_type), None) | |
| u_value = selected_roof.u_value if selected_roof else 0.25 | |
| st.write(f"U-Value: {u_value:.3f} W/m²K") | |
| # Roof dimensions | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| roof_length = st.number_input( | |
| "Roof Length (m):", | |
| min_value=0.1, | |
| max_value=100.0, | |
| value=20.0 if not roof else (roof.area / 10.0), # Assuming width of 10m if editing | |
| step=0.1 | |
| ) | |
| with col2: | |
| roof_width = st.number_input( | |
| "Roof Width (m):", | |
| min_value=0.1, | |
| max_value=100.0, | |
| value=10.0 if not roof else (roof.area / roof_length), | |
| step=0.1 | |
| ) | |
| # Calculate roof area | |
| roof_area = roof_length * roof_width | |
| st.write(f"Roof Area: {roof_area:.2f} m²") | |
| # Roof pitch | |
| roof_pitch = st.slider( | |
| "Roof Pitch (°):", | |
| min_value=0.0, | |
| max_value=60.0, | |
| value=roof.pitch if roof else 0.0, | |
| step=1.0 | |
| ) | |
| # Suspended ceiling | |
| has_suspended_ceiling = st.checkbox( | |
| "Has Suspended Ceiling", | |
| value=roof.has_suspended_ceiling if roof else False | |
| ) | |
| ceiling_plenum_height = 0.0 | |
| if has_suspended_ceiling: | |
| ceiling_plenum_height = st.number_input( | |
| "Ceiling Plenum Height (m):", | |
| min_value=0.1, | |
| max_value=5.0, | |
| value=roof.ceiling_plenum_height if roof and roof.has_suspended_ceiling else 0.5, | |
| step=0.1 | |
| ) | |
| # Submit button | |
| submit_label = "Update Roof" if editing_roof else "Add Roof" | |
| submit = st.form_submit_button(submit_label) | |
| if submit and not session_state["form_submitted"]["roof_form"]: | |
| # Set the form submission flag to prevent infinite loops | |
| session_state["form_submitted"]["roof_form"] = True | |
| # Create or update roof | |
| if roof_type == "Custom": | |
| # Create custom roof | |
| new_roof = Roof( | |
| id=f"custom_roof_{str(uuid.uuid4())[:8]}", | |
| name=roof_name, | |
| component_type=ComponentType.ROOF, | |
| u_value=u_value, | |
| area=roof_area, | |
| orientation=Orientation.HORIZONTAL, | |
| roof_type="Custom", | |
| roof_group="Custom", | |
| pitch=roof_pitch, | |
| has_suspended_ceiling=has_suspended_ceiling, | |
| ceiling_plenum_height=ceiling_plenum_height | |
| ) | |
| else: | |
| # Clone preset roof and update properties | |
| selected_roof = next((r for r in preset_roofs if r.name == roof_type), None) | |
| if selected_roof: | |
| new_roof = Roof( | |
| id=f"custom_roof_{str(uuid.uuid4())[:8]}", | |
| name=roof_name, | |
| component_type=ComponentType.ROOF, | |
| u_value=selected_roof.u_value, | |
| area=roof_area, | |
| orientation=Orientation.HORIZONTAL, | |
| roof_type=selected_roof.roof_type, | |
| roof_group=selected_roof.roof_group, | |
| pitch=roof_pitch, | |
| has_suspended_ceiling=has_suspended_ceiling, | |
| ceiling_plenum_height=ceiling_plenum_height, | |
| material_layers=selected_roof.material_layers | |
| ) | |
| # Add or update roof in session state | |
| if editing_roof: | |
| session_state["components"]["roofs"][roof_to_edit] = new_roof | |
| del session_state["roof_to_edit"] | |
| else: | |
| session_state["components"]["roofs"].append(new_roof) | |
| st.experimental_rerun() | |
| def _display_floor_selection(self, session_state: Dict[str, Any]) -> None: | |
| """ | |
| Display floor selection interface. | |
| Args: | |
| session_state: Streamlit session state for storing form data | |
| """ | |
| st.subheader("Floors") | |
| # Display existing floors | |
| if session_state["components"]["floors"]: | |
| st.write("Existing Floors:") | |
| # Create a table of existing floors | |
| floor_data = [] | |
| for i, floor in enumerate(session_state["components"]["floors"]): | |
| floor_data.append({ | |
| "ID": i + 1, | |
| "Name": floor.name, | |
| "Type": floor.floor_type, | |
| "U-Value (W/m²K)": f"{floor.u_value:.3f}", | |
| "Area (m²)": f"{floor.area:.2f}", | |
| "Ground Contact": "Yes" if floor.is_ground_contact else "No" | |
| }) | |
| floor_df = pd.DataFrame(floor_data) | |
| st.dataframe(floor_df) | |
| # Add edit and delete buttons for each floor | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| floor_to_edit = st.selectbox( | |
| "Select floor to edit:", | |
| options=range(1, len(session_state["components"]["floors"]) + 1), | |
| format_func=lambda x: f"Floor #{x}: {session_state['components']['floors'][x-1].name}" | |
| ) | |
| with col2: | |
| edit_col, delete_col = st.columns(2) | |
| with edit_col: | |
| if st.button("Edit Floor", key="edit_floor"): | |
| # Set the floor to edit in session state | |
| session_state["floor_to_edit"] = floor_to_edit - 1 | |
| with delete_col: | |
| if st.button("Delete Floor", key="delete_floor"): | |
| # Queue the delete action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["delete_floor"] = floor_to_edit - 1 | |
| # Add new floor form | |
| st.write("Add New Floor:") | |
| # Check if we're editing an existing floor | |
| editing_floor = "floor_to_edit" in session_state | |
| floor_to_edit = session_state.get("floor_to_edit", None) | |
| # Get the floor to edit if we're editing | |
| floor = None | |
| if editing_floor and floor_to_edit is not None: | |
| floor = session_state["components"]["floors"][floor_to_edit] | |
| # Floor form | |
| with st.form(key="floor_form"): | |
| # Floor name | |
| floor_name = st.text_input( | |
| "Floor Name:", | |
| value=floor.name if floor else "" | |
| ) | |
| # Floor type selection | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Get preset floor types | |
| preset_floors = self.component_library.get_preset_components_by_type(ComponentType.FLOOR) | |
| preset_floor_names = [floor.name for floor in preset_floors] | |
| floor_type = st.selectbox( | |
| "Floor Type:", | |
| options=["Custom"] + preset_floor_names, | |
| index=preset_floor_names.index(floor.floor_type) + 1 if floor and floor.floor_type in preset_floor_names else 0 | |
| ) | |
| # U-value | |
| with col2: | |
| if floor_type == "Custom": | |
| u_value = st.number_input( | |
| "U-Value (W/m²K):", | |
| min_value=0.0, | |
| max_value=10.0, | |
| value=floor.u_value if floor else 0.25, | |
| step=0.01 | |
| ) | |
| else: | |
| # Get U-value from preset floor | |
| selected_floor = next((f for f in preset_floors if f.name == floor_type), None) | |
| u_value = selected_floor.u_value if selected_floor else 0.25 | |
| st.write(f"U-Value: {u_value:.3f} W/m²K") | |
| # Floor dimensions | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| floor_length = st.number_input( | |
| "Floor Length (m):", | |
| min_value=0.1, | |
| max_value=100.0, | |
| value=20.0 if not floor else (floor.area / 10.0), # Assuming width of 10m if editing | |
| step=0.1 | |
| ) | |
| with col2: | |
| floor_width = st.number_input( | |
| "Floor Width (m):", | |
| min_value=0.1, | |
| max_value=100.0, | |
| value=10.0 if not floor else (floor.area / floor_length), | |
| step=0.1 | |
| ) | |
| # Calculate floor area | |
| floor_area = floor_length * floor_width | |
| st.write(f"Floor Area: {floor_area:.2f} m²") | |
| # Ground contact | |
| is_ground_contact = st.checkbox( | |
| "Is in Contact with Ground", | |
| value=floor.is_ground_contact if floor else False | |
| ) | |
| perimeter_length = 0.0 | |
| if is_ground_contact: | |
| perimeter_length = st.number_input( | |
| "Perimeter Length (m):", | |
| min_value=0.1, | |
| max_value=1000.0, | |
| value=floor.perimeter_length if floor and floor.is_ground_contact else 2 * (floor_length + floor_width), | |
| step=0.1 | |
| ) | |
| # Submit button | |
| submit_label = "Update Floor" if editing_floor else "Add Floor" | |
| submit = st.form_submit_button(submit_label) | |
| if submit and not session_state["form_submitted"]["floor_form"]: | |
| # Set the form submission flag to prevent infinite loops | |
| session_state["form_submitted"]["floor_form"] = True | |
| # Create or update floor | |
| if floor_type == "Custom": | |
| # Create custom floor | |
| new_floor = Floor( | |
| id=f"custom_floor_{str(uuid.uuid4())[:8]}", | |
| name=floor_name, | |
| component_type=ComponentType.FLOOR, | |
| u_value=u_value, | |
| area=floor_area, | |
| orientation=Orientation.HORIZONTAL, | |
| floor_type="Custom", | |
| is_ground_contact=is_ground_contact, | |
| perimeter_length=perimeter_length | |
| ) | |
| else: | |
| # Clone preset floor and update properties | |
| selected_floor = next((f for f in preset_floors if f.name == floor_type), None) | |
| if selected_floor: | |
| new_floor = Floor( | |
| id=f"custom_floor_{str(uuid.uuid4())[:8]}", | |
| name=floor_name, | |
| component_type=ComponentType.FLOOR, | |
| u_value=selected_floor.u_value, | |
| area=floor_area, | |
| orientation=Orientation.HORIZONTAL, | |
| floor_type=selected_floor.floor_type, | |
| is_ground_contact=is_ground_contact, | |
| perimeter_length=perimeter_length, | |
| material_layers=selected_floor.material_layers | |
| ) | |
| # Add or update floor in session state | |
| if editing_floor: | |
| session_state["components"]["floors"][floor_to_edit] = new_floor | |
| del session_state["floor_to_edit"] | |
| else: | |
| # Queue the add action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["add_floor"] = new_floor | |
| def _display_window_selection(self, session_state: Dict[str, Any]) -> None: | |
| """ | |
| Display window selection interface. | |
| Args: | |
| session_state: Streamlit session state for storing form data | |
| """ | |
| st.subheader("Windows") | |
| # Display existing windows | |
| if session_state["components"]["windows"]: | |
| st.write("Existing Windows:") | |
| # Create a table of existing windows | |
| window_data = [] | |
| for i, window in enumerate(session_state["components"]["windows"]): | |
| window_data.append({ | |
| "ID": i + 1, | |
| "Name": window.name, | |
| "Type": window.window_type, | |
| "U-Value (W/m²K)": f"{window.u_value:.3f}", | |
| "SHGC": f"{window.shgc:.2f}", | |
| "Area (m²)": f"{window.area:.2f}", | |
| "Orientation": window.orientation.value | |
| }) | |
| window_df = pd.DataFrame(window_data) | |
| st.dataframe(window_df) | |
| # Add edit and delete buttons for each window | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| window_to_edit = st.selectbox( | |
| "Select window to edit:", | |
| options=range(1, len(session_state["components"]["windows"]) + 1), | |
| format_func=lambda x: f"Window #{x}: {session_state['components']['windows'][x-1].name}" | |
| ) | |
| with col2: | |
| edit_col, delete_col = st.columns(2) | |
| with edit_col: | |
| if st.button("Edit Window", key="edit_window"): | |
| # Set the window to edit in session state | |
| session_state["window_to_edit"] = window_to_edit - 1 | |
| with delete_col: | |
| if st.button("Delete Window", key="delete_window"): | |
| # Queue the delete action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["delete_window"] = window_to_edit - 1 | |
| # Add new window form | |
| st.write("Add New Window:") | |
| # Check if we're editing an existing window | |
| editing_window = "window_to_edit" in session_state | |
| window_to_edit = session_state.get("window_to_edit", None) | |
| # Get the window to edit if we're editing | |
| window = None | |
| if editing_window and window_to_edit is not None: | |
| window = session_state["components"]["windows"][window_to_edit] | |
| # Window form | |
| with st.form(key="window_form"): | |
| # Window name | |
| window_name = st.text_input( | |
| "Window Name:", | |
| value=window.name if window else "" | |
| ) | |
| # Window type selection | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Get preset window types | |
| preset_windows = self.component_library.get_preset_components_by_type(ComponentType.WINDOW) | |
| preset_window_names = [window.name for window in preset_windows] | |
| window_type = st.selectbox( | |
| "Window Type:", | |
| options=["Custom"] + preset_window_names, | |
| index=preset_window_names.index(window.window_type) + 1 if window and window.window_type in preset_window_names else 0 | |
| ) | |
| # U-value and SHGC | |
| with col2: | |
| if window_type == "Custom": | |
| u_value = st.number_input( | |
| "U-Value (W/m²K):", | |
| min_value=0.0, | |
| max_value=10.0, | |
| value=window.u_value if window else 1.5, | |
| step=0.01 | |
| ) | |
| shgc = st.number_input( | |
| "Solar Heat Gain Coefficient (SHGC):", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=window.shgc if window else 0.7, | |
| step=0.01 | |
| ) | |
| vt = st.number_input( | |
| "Visible Transmittance (VT):", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=window.vt if window else 0.7, | |
| step=0.01 | |
| ) | |
| else: | |
| # Get properties from preset window | |
| selected_window = next((w for w in preset_windows if w.name == window_type), None) | |
| if selected_window: | |
| u_value = selected_window.u_value | |
| shgc = selected_window.shgc | |
| vt = selected_window.vt | |
| st.write(f"U-Value: {u_value:.3f} W/m²K") | |
| st.write(f"SHGC: {shgc:.2f}") | |
| st.write(f"VT: {vt:.2f}") | |
| # Window dimensions | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| window_height = st.number_input( | |
| "Window Height (m):", | |
| min_value=0.1, | |
| max_value=10.0, | |
| value=1.5 if not window else (window.area / 2.0), # Assuming width of 2m if editing | |
| step=0.1 | |
| ) | |
| with col2: | |
| window_width = st.number_input( | |
| "Window Width (m):", | |
| min_value=0.1, | |
| max_value=10.0, | |
| value=2.0 if not window else (window.area / window_height), | |
| step=0.1 | |
| ) | |
| # Calculate window area | |
| window_area = window_height * window_width | |
| st.write(f"Window Area: {window_area:.2f} m²") | |
| # Window orientation | |
| orientation_options = [ | |
| Orientation.NORTH, | |
| Orientation.NORTHEAST, | |
| Orientation.EAST, | |
| Orientation.SOUTHEAST, | |
| Orientation.SOUTH, | |
| Orientation.SOUTHWEST, | |
| Orientation.WEST, | |
| Orientation.NORTHWEST | |
| ] | |
| orientation = st.selectbox( | |
| "Orientation:", | |
| options=orientation_options, | |
| index=orientation_options.index(window.orientation) if window and window.orientation in orientation_options else 0, | |
| format_func=lambda x: x.value | |
| ) | |
| # Shading | |
| has_shading = st.checkbox( | |
| "Has Shading", | |
| value=window.has_shading if window else False | |
| ) | |
| shading_type = None | |
| shading_coefficient = 1.0 | |
| if has_shading: | |
| shading_options = ["Internal", "External", "Between-glass"] | |
| shading_type = st.selectbox( | |
| "Shading Type:", | |
| options=shading_options, | |
| index=shading_options.index(window.shading_type) if window and window.shading_type in shading_options else 0 | |
| ) | |
| shading_coefficient = st.slider( | |
| "Shading Coefficient:", | |
| min_value=0.1, | |
| max_value=1.0, | |
| value=window.shading_coefficient if window and window.has_shading else 0.7, | |
| step=0.1 | |
| ) | |
| # Wall assignment | |
| if session_state["components"]["walls"]: | |
| wall_options = [wall.name for wall in session_state["components"]["walls"]] | |
| assigned_wall = st.selectbox( | |
| "Assign to Wall:", | |
| options=["None"] + wall_options, | |
| index=0 | |
| ) | |
| else: | |
| assigned_wall = "None" | |
| st.warning("No walls available for assignment. Please add walls first.") | |
| # Submit button | |
| submit_label = "Update Window" if editing_window else "Add Window" | |
| submit = st.form_submit_button(submit_label) | |
| if submit and not session_state["form_submitted"]["window_form"]: | |
| # Set the form submission flag to prevent infinite loops | |
| session_state["form_submitted"]["window_form"] = True | |
| # Create or update window | |
| if window_type == "Custom": | |
| # Create custom window | |
| new_window = Window( | |
| id=f"custom_window_{str(uuid.uuid4())[:8]}", | |
| name=window_name, | |
| component_type=ComponentType.WINDOW, | |
| u_value=u_value, | |
| area=window_area, | |
| orientation=orientation, | |
| shgc=shgc, | |
| vt=vt, | |
| window_type="Custom", | |
| glazing_layers=2, | |
| gas_fill="Air", | |
| low_e_coating=False, | |
| has_shading=has_shading, | |
| shading_type=shading_type, | |
| shading_coefficient=shading_coefficient | |
| ) | |
| else: | |
| # Clone preset window and update properties | |
| selected_window = next((w for w in preset_windows if w.name == window_type), None) | |
| if selected_window: | |
| new_window = Window( | |
| id=f"custom_window_{str(uuid.uuid4())[:8]}", | |
| name=window_name, | |
| component_type=ComponentType.WINDOW, | |
| u_value=selected_window.u_value, | |
| area=window_area, | |
| orientation=orientation, | |
| shgc=selected_window.shgc, | |
| vt=selected_window.vt, | |
| window_type=selected_window.window_type, | |
| glazing_layers=selected_window.glazing_layers, | |
| gas_fill=selected_window.gas_fill, | |
| low_e_coating=selected_window.low_e_coating, | |
| has_shading=has_shading, | |
| shading_type=shading_type, | |
| shading_coefficient=shading_coefficient | |
| ) | |
| # Add or update window in session state | |
| if editing_window: | |
| session_state["components"]["windows"][window_to_edit] = new_window | |
| del session_state["window_to_edit"] | |
| else: | |
| # Queue the add action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["add_window"] = new_window | |
| # Assign window to wall if selected | |
| if assigned_wall != "None": | |
| # Find the wall by name | |
| for wall in session_state["components"]["walls"]: | |
| if wall.name == assigned_wall: | |
| # Add window ID to wall's windows list | |
| if new_window.id not in wall.windows: | |
| wall.windows.append(new_window.id) | |
| # Update wall's net area | |
| if wall.gross_area is None: | |
| wall.gross_area = wall.area | |
| wall.net_area = wall.gross_area - sum( | |
| w.area for w in session_state["components"]["windows"] | |
| if w.id in wall.windows | |
| ) - sum( | |
| d.area for d in session_state["components"]["doors"] | |
| if d.id in wall.doors | |
| ) | |
| wall.area = wall.net_area | |
| break | |
| def _display_door_selection(self, session_state: Dict[str, Any]) -> None: | |
| """ | |
| Display door selection interface. | |
| Args: | |
| session_state: Streamlit session state for storing form data | |
| """ | |
| st.subheader("Doors") | |
| # Display existing doors | |
| if session_state["components"]["doors"]: | |
| st.write("Existing Doors:") | |
| # Create a table of existing doors | |
| door_data = [] | |
| for i, door in enumerate(session_state["components"]["doors"]): | |
| door_data.append({ | |
| "ID": i + 1, | |
| "Name": door.name, | |
| "Type": door.door_type, | |
| "U-Value (W/m²K)": f"{door.u_value:.3f}", | |
| "Area (m²)": f"{door.area:.2f}", | |
| "Glazing %": f"{door.glazing_percentage:.0f}%", | |
| "Orientation": door.orientation.value | |
| }) | |
| door_df = pd.DataFrame(door_data) | |
| st.dataframe(door_df) | |
| # Add edit and delete buttons for each door | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| door_to_edit = st.selectbox( | |
| "Select door to edit:", | |
| options=range(1, len(session_state["components"]["doors"]) + 1), | |
| format_func=lambda x: f"Door #{x}: {session_state['components']['doors'][x-1].name}" | |
| ) | |
| with col2: | |
| edit_col, delete_col = st.columns(2) | |
| with edit_col: | |
| if st.button("Edit Door", key="edit_door"): | |
| # Set the door to edit in session state | |
| session_state["door_to_edit"] = door_to_edit - 1 | |
| with delete_col: | |
| if st.button("Delete Door", key="delete_door"): | |
| # Queue the delete action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["delete_door"] = door_to_edit - 1 | |
| # Add new door form | |
| st.write("Add New Door:") | |
| # Check if we're editing an existing door | |
| editing_door = "door_to_edit" in session_state | |
| door_to_edit = session_state.get("door_to_edit", None) | |
| # Get the door to edit if we're editing | |
| door = None | |
| if editing_door and door_to_edit is not None: | |
| door = session_state["components"]["doors"][door_to_edit] | |
| # Door form | |
| with st.form(key="door_form"): | |
| # Door name | |
| door_name = st.text_input( | |
| "Door Name:", | |
| value=door.name if door else "" | |
| ) | |
| # Door type selection | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| # Get preset door types | |
| preset_doors = self.component_library.get_preset_components_by_type(ComponentType.DOOR) | |
| preset_door_names = [door.name for door in preset_doors] | |
| door_type = st.selectbox( | |
| "Door Type:", | |
| options=["Custom"] + preset_door_names, | |
| index=preset_door_names.index(door.door_type) + 1 if door and door.door_type in preset_door_names else 0 | |
| ) | |
| # U-value and glazing percentage | |
| with col2: | |
| if door_type == "Custom": | |
| u_value = st.number_input( | |
| "U-Value (W/m²K):", | |
| min_value=0.0, | |
| max_value=10.0, | |
| value=door.u_value if door else 2.0, | |
| step=0.01 | |
| ) | |
| glazing_percentage = st.slider( | |
| "Glazing Percentage (%):", | |
| min_value=0, | |
| max_value=100, | |
| value=int(door.glazing_percentage) if door else 0, | |
| step=5 | |
| ) | |
| # Only show SHGC and VT if door has glazing | |
| if glazing_percentage > 0: | |
| shgc = st.number_input( | |
| "Solar Heat Gain Coefficient (SHGC):", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=door.shgc if door and door.shgc else 0.7, | |
| step=0.01 | |
| ) | |
| vt = st.number_input( | |
| "Visible Transmittance (VT):", | |
| min_value=0.0, | |
| max_value=1.0, | |
| value=door.vt if door and door.vt else 0.7, | |
| step=0.01 | |
| ) | |
| else: | |
| shgc = 0.0 | |
| vt = 0.0 | |
| else: | |
| # Get properties from preset door | |
| selected_door = next((d for d in preset_doors if d.name == door_type), None) | |
| if selected_door: | |
| u_value = selected_door.u_value | |
| glazing_percentage = selected_door.glazing_percentage | |
| shgc = selected_door.shgc | |
| vt = selected_door.vt | |
| st.write(f"U-Value: {u_value:.3f} W/m²K") | |
| st.write(f"Glazing: {glazing_percentage:.0f}%") | |
| if glazing_percentage > 0: | |
| st.write(f"SHGC: {shgc:.2f}") | |
| st.write(f"VT: {vt:.2f}") | |
| # Door dimensions | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| door_height = st.number_input( | |
| "Door Height (m):", | |
| min_value=0.1, | |
| max_value=5.0, | |
| value=2.1 if not door else (door.area / 0.9), # Assuming width of 0.9m if editing | |
| step=0.1 | |
| ) | |
| with col2: | |
| door_width = st.number_input( | |
| "Door Width (m):", | |
| min_value=0.1, | |
| max_value=5.0, | |
| value=0.9 if not door else (door.area / door_height), | |
| step=0.1 | |
| ) | |
| # Calculate door area | |
| door_area = door_height * door_width | |
| st.write(f"Door Area: {door_area:.2f} m²") | |
| # Door orientation | |
| orientation_options = [ | |
| Orientation.NORTH, | |
| Orientation.NORTHEAST, | |
| Orientation.EAST, | |
| Orientation.SOUTHEAST, | |
| Orientation.SOUTH, | |
| Orientation.SOUTHWEST, | |
| Orientation.WEST, | |
| Orientation.NORTHWEST | |
| ] | |
| orientation = st.selectbox( | |
| "Orientation:", | |
| options=orientation_options, | |
| index=orientation_options.index(door.orientation) if door and door.orientation in orientation_options else 0, | |
| format_func=lambda x: x.value | |
| ) | |
| # Wall assignment | |
| if session_state["components"]["walls"]: | |
| wall_options = [wall.name for wall in session_state["components"]["walls"]] | |
| assigned_wall = st.selectbox( | |
| "Assign to Wall:", | |
| options=["None"] + wall_options, | |
| index=0 | |
| ) | |
| else: | |
| assigned_wall = "None" | |
| st.warning("No walls available for assignment. Please add walls first.") | |
| # Submit button | |
| submit_label = "Update Door" if editing_door else "Add Door" | |
| submit = st.form_submit_button(submit_label) | |
| if submit and not session_state["form_submitted"]["door_form"]: | |
| # Set the form submission flag to prevent infinite loops | |
| session_state["form_submitted"]["door_form"] = True | |
| # Create or update door | |
| if door_type == "Custom": | |
| # Create custom door | |
| new_door = Door( | |
| id=f"custom_door_{str(uuid.uuid4())[:8]}", | |
| name=door_name, | |
| component_type=ComponentType.DOOR, | |
| u_value=u_value, | |
| area=door_area, | |
| orientation=orientation, | |
| door_type="Custom", | |
| glazing_percentage=glazing_percentage, | |
| shgc=shgc if glazing_percentage > 0 else 0.0, | |
| vt=vt if glazing_percentage > 0 else 0.0 | |
| ) | |
| else: | |
| # Clone preset door and update properties | |
| selected_door = next((d for d in preset_doors if d.name == door_type), None) | |
| if selected_door: | |
| new_door = Door( | |
| id=f"custom_door_{str(uuid.uuid4())[:8]}", | |
| name=door_name, | |
| component_type=ComponentType.DOOR, | |
| u_value=selected_door.u_value, | |
| area=door_area, | |
| orientation=orientation, | |
| door_type=selected_door.door_type, | |
| glazing_percentage=selected_door.glazing_percentage, | |
| shgc=selected_door.shgc, | |
| vt=selected_door.vt | |
| ) | |
| # Add or update door in session state | |
| if editing_door: | |
| session_state["components"]["doors"][door_to_edit] = new_door | |
| del session_state["door_to_edit"] | |
| else: | |
| # Queue the add action in pending_actions instead of doing it immediately | |
| session_state["pending_actions"]["add_door"] = new_door | |
| # Assign door to wall if selected | |
| if assigned_wall != "None": | |
| # Find the wall by name | |
| for wall in session_state["components"]["walls"]: | |
| if wall.name == assigned_wall: | |
| # Add door ID to wall's doors list | |
| if new_door.id not in wall.doors: | |
| wall.doors.append(new_door.id) | |
| # Update wall's net area | |
| if wall.gross_area is None: | |
| wall.gross_area = wall.area | |
| wall.net_area = wall.gross_area - sum( | |
| w.area for w in session_state["components"]["windows"] | |
| if w.id in wall.windows | |
| ) - sum( | |
| d.area for d in session_state["components"]["doors"] | |
| if d.id in wall.doors | |
| ) | |
| wall.area = wall.net_area | |
| break | |