Spaces:
Sleeping
Sleeping
| """ | |
| BuildSustain - Main Module | |
| This is the main module for the BuildSustain application. It serves as the central | |
| orchestrator for the application, handling navigation between different modules and managing | |
| the overall application flow. | |
| Developed by: Dr Majed Abuseif, Deakin University | |
| © 2025 | |
| """ | |
| import streamlit as st | |
| import logging | |
| import os | |
| import sys | |
| from typing import Dict, List, Any, Optional, Tuple | |
| from datetime import datetime | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') | |
| logger = logging.getLogger(__name__) | |
| # Add the current directory to the path so we can import our modules | |
| sys.path.append(os.path.dirname(os.path.abspath(__file__))) | |
| # Import modules as they are developed | |
| from app.intro import display_intro_page | |
| from app.building_information import display_building_info_page | |
| from app.climate_data import display_climate_page | |
| from app.materials_library import display_materials_page, Material, GlazingMaterial, MaterialCategory, MaterialLibrary | |
| from app.construction import display_construction_page, get_available_constructions | |
| from app.components import display_components_page | |
| from app.internal_loads import display_internal_loads_page | |
| from app.hvac_loads import display_hvac_loads_page | |
| from app.building_energy import display_building_energy_page | |
| from app.renewable_energy import display_renewable_energy_page | |
| from app.embodied_energy import display_embodied_energy_page | |
| from app.materials_cost import display_materials_cost_page | |
| from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, DEFAULT_MATERIAL_PROPERTIES, DEFAULT_WINDOW_PROPERTIES, SAMPLE_CONSTRUCTIONS | |
| class BuildSustain: | |
| """ | |
| Main class for the BuildSustain application. | |
| Handles navigation, session state initialization, and module integration. | |
| """ | |
| def __init__(self): | |
| """ | |
| Initialize the BuildSustain application. | |
| Sets up the page configuration and initializes session state if needed. | |
| """ | |
| # Configure the Streamlit page | |
| st.set_page_config( | |
| page_title="BuildSustain", | |
| page_icon="🌡️", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Initialize session state if it doesn't exist | |
| self._initialize_session_state() | |
| # Set up the application layout | |
| self.setup_layout() | |
| def _initialize_session_state(self): | |
| """ | |
| Initialize the session state structure if it doesn't exist. | |
| This creates the basic structure for storing all application data. | |
| """ | |
| # Initialize current page if not set | |
| if 'current_page' not in st.session_state: | |
| st.session_state.current_page = "Intro" | |
| # Initialize material library if not set | |
| if 'material_library' not in st.session_state: | |
| st.session_state.material_library = MaterialLibrary() | |
| logger.info("Initialized MaterialLibrary in session state") | |
| # Initialize project data structure if not set | |
| if 'project_data' not in st.session_state: | |
| st.session_state.project_data = { | |
| "project_name": "", | |
| "building_info": { | |
| "project_name": "", | |
| "floor_area": 100.0, | |
| "building_height": 3.0, | |
| "building_type": "Office", | |
| "summer_indoor_design_temp": 24.0, | |
| "summer_indoor_design_rh": 50.0, | |
| "winter_indoor_design_temp": 20.0, | |
| "winter_indoor_design_rh": 50.0, | |
| "orientation_angle": 0.0 | |
| }, | |
| "climate_data": { | |
| "id": "", | |
| "location": { | |
| "city": "", | |
| "state_province": "", | |
| "country": "", | |
| "source": "", | |
| "wmo": "", | |
| "latitude": 0.0, | |
| "longitude": 0.0, | |
| "timezone": 0.0, | |
| "elevation": 0.0 | |
| }, | |
| "design_conditions": { | |
| "winter_design_temp": 0.0, | |
| "summer_design_temp_db": 30.0, | |
| "summer_design_temp_wb": 25.0, | |
| "heating_degree_days": 0, | |
| "cooling_degree_days": 0, | |
| "monthly_average_temps": [20.0] * 12, | |
| "monthly_average_radiation": [150.0] * 12, | |
| "summer_daily_range": 8.0, | |
| "wind_speed": 3.0, | |
| "pressure": 101325.0 | |
| }, | |
| "climate_zone": "", | |
| "hourly_data": [], | |
| "epw_filename": "", | |
| "typical_extreme_periods": { | |
| "summer_extreme": {"start": {"month": 7, "day": 1}, "end": {"month": 7, "day": 7}}, | |
| "summer_typical": {"start": {"month": 6, "day": 1}, "end": {"month": 6, "day": 7}}, | |
| "winter_extreme": {"start": {"month": 1, "day": 1}, "end": {"month": 1, "day": 7}}, | |
| "winter_typical": {"start": {"month": 12, "day": 1}, "end": {"month": 12, "day": 7}} | |
| }, | |
| "ground_temperatures": { | |
| "0.5": [20.0] * 12, | |
| "2": [18.0] * 12, | |
| "4": [16.0] * 12 | |
| }, | |
| "ground_reflectivity": 0.2 | |
| }, | |
| "materials": { | |
| "library": dict(SAMPLE_MATERIALS), | |
| "project": {} | |
| }, | |
| "fenestrations": { | |
| "library": dict(SAMPLE_FENESTRATIONS), | |
| "project": {} | |
| }, | |
| "constructions": { | |
| "library": dict(SAMPLE_CONSTRUCTIONS), | |
| "project": {} | |
| }, | |
| "components": { | |
| "walls": [], | |
| "roofs": [], | |
| "floors": [], | |
| "windows": [], | |
| "skylights": [] | |
| }, | |
| "internal_loads": { | |
| "schedules": {}, | |
| "people": [], | |
| "lighting": [], | |
| "equipment": [], | |
| "ventilation": [], | |
| "infiltration": [] | |
| }, | |
| "internal_loads_conditions": { | |
| "air_velocity": 0.1, | |
| "lighting_convective_fraction": 0.5, | |
| "lighting_radiative_fraction": 0.5, | |
| "equipment_convective_fraction": 0.5, | |
| "equipment_radiative_fraction": 0.5 | |
| }, | |
| "hvac_loads": { | |
| "cooling": { | |
| "hourly": [], | |
| "peak": 0.0, | |
| "summary_tables": {}, | |
| "charts": { | |
| "pie_by_component": {}, | |
| "pie_by_orientation": {} | |
| }, | |
| "breakdown": { | |
| "conduction": 0.0, | |
| "solar": 0.0, | |
| "internal": 0.0, | |
| "ventilation_sensible": 0.0, | |
| "ventilation_latent": 0.0, | |
| "infiltration_sensible": 0.0, | |
| "infiltration_latent": 0.0 | |
| } | |
| }, | |
| "heating": { | |
| "hourly": [], | |
| "peak": 0.0, | |
| "summary_tables": {}, | |
| "charts": { | |
| "pie_by_component": {}, | |
| "pie_by_orientation": {} | |
| }, | |
| "breakdown": { | |
| "conduction": 0.0, | |
| "ventilation": 0.0, | |
| "infiltration": 0.0 | |
| } | |
| }, | |
| "monthly_summary": {} | |
| }, | |
| "hvac_settings": { | |
| "operating_hours": [{"start": 8, "end": 18}], | |
| "system_type": "Default" | |
| }, | |
| "sim_period": { | |
| "type": "Full Year", | |
| "start_date": datetime(2025, 1, 1), | |
| "end_date": datetime(2025, 12, 31), | |
| "base_temp": 18.3 | |
| }, | |
| "indoor_conditions": { | |
| "type": "Fixed Setpoints", | |
| "cooling_setpoint": {"temperature": 24.0, "rh": 50.0}, | |
| "heating_setpoint": {"temperature": 20.0, "rh": 50.0}, | |
| "adaptive_acceptability": "90", | |
| "schedule": [] | |
| }, | |
| "building_energy": { | |
| "hvac_type": "", | |
| "cop": 0.0, | |
| "energy_consumption": { | |
| "cooling": 0, | |
| "heating": 0, | |
| "lighting": 0, | |
| "equipment": 0, | |
| "total": 0 | |
| }, | |
| "charts": {} | |
| }, | |
| "renewable_energy": { | |
| "pv_system_size_kw": 0, | |
| "pv_generation_kwh": 0, | |
| "net_energy_kwh": 0, | |
| "zero_energy_status": "", | |
| "charts": {} | |
| }, | |
| "embodied_energy": { | |
| "total_embodied_carbon_kgco2e": 0, | |
| "breakdown_by_component": {}, | |
| "charts": {}, | |
| "material_inventory": [] | |
| }, | |
| "materials_cost": { | |
| "total_cost_usd": 0, | |
| "breakdown_by_component": {}, | |
| "charts": {}, | |
| "material_inventory": [] | |
| } | |
| } | |
| # Initialize UI state variables | |
| if 'debug_mode' not in st.session_state: | |
| st.session_state.debug_mode = False | |
| # Initialize module-specific rerun flags | |
| if 'module_rerun_flags' not in st.session_state: | |
| st.session_state.module_rerun_flags = {} | |
| def setup_layout(self): | |
| """ | |
| Set up the main application layout with sidebar navigation and main content area. | |
| """ | |
| # Create the sidebar | |
| st.sidebar.title("BuildSustain") | |
| st.sidebar.markdown("---") | |
| # Navigation section in sidebar | |
| st.sidebar.subheader("Navigation") | |
| # Define all available pages | |
| pages = [ | |
| "Intro", | |
| "Building Information", | |
| "Climate Data", | |
| "Material Library", | |
| "Construction", | |
| "Building Components", | |
| "Internal Loads", | |
| "HVAC Loads", | |
| "Building Energy", | |
| "Renewable Energy", | |
| "Embodied Energy", | |
| "Materials Cost" | |
| ] | |
| # Store previous page for comparison | |
| previous_page = st.session_state.current_page | |
| # Create the navigation radio buttons | |
| selected_page = st.sidebar.radio( | |
| "Go to", | |
| pages, | |
| index=pages.index(st.session_state.current_page) | |
| ) | |
| # Update the current page if changed | |
| if selected_page != st.session_state.current_page: | |
| st.session_state.current_page = selected_page | |
| # Clear module-specific states when leaving certain pages | |
| self._handle_page_transition(previous_page, selected_page) | |
| # Add application info to sidebar | |
| st.sidebar.markdown("---") | |
| st.sidebar.info( | |
| "BuildSustain Beta Version 0.4.1\n\n" | |
| "Developed by: Dr Majed Abuseif\n\n" | |
| "School of Architecture and Built Environment\n\n" | |
| "Deakin University\n\n" | |
| "© 2025" | |
| ) | |
| # Add help section to sidebar | |
| st.sidebar.markdown("### Help") | |
| st.sidebar.write("Learn about the used methods:") | |
| st.sidebar.markdown("[ASHRAE Handbook](https://www.ashrae.org/technical-resources/ashrae-handbook/ashrae-handbook-online)") | |
| # Debug mode toggle (for development) | |
| if st.sidebar.checkbox("Debug Mode", value=st.session_state.debug_mode): | |
| st.session_state.debug_mode = True | |
| else: | |
| st.session_state.debug_mode = False | |
| # Display the selected page content | |
| self.display_page(st.session_state.current_page) | |
| # Handle module-specific reruns | |
| self._handle_module_reruns() | |
| def _handle_page_transition(self, previous_page: str, new_page: str): | |
| """ | |
| Handle cleanup when transitioning between pages. | |
| Only clear states that are specific to the page being left. | |
| """ | |
| # Clear materials library specific states when leaving that page | |
| if previous_page == "Material Library": | |
| keys_to_clear = [ | |
| "material_editor", | |
| "fenestration_editor", | |
| "material_saved", | |
| "fenestration_saved", | |
| "material_action", | |
| "fenestration_action", | |
| "materials_rerun_pending", | |
| "material_form_state", | |
| "fenestration_form_state", | |
| "rerun_trigger", | |
| "active_tab" | |
| ] | |
| for key in keys_to_clear: | |
| if key in st.session_state: | |
| st.session_state.pop(key, None) | |
| # Clear climate data specific states when leaving the Climate Data page | |
| if previous_page == "Climate Data": | |
| keys_to_clear = [ | |
| "data_source", | |
| "projection_country", | |
| "projection_state", | |
| "location", | |
| "rcp", | |
| "year", | |
| "climate_tab" | |
| ] | |
| for key in keys_to_clear: | |
| if key in st.session_state: | |
| st.session_state.pop(key, None) | |
| # Clear construction-specific states when leaving the Construction page | |
| if previous_page == "Construction": | |
| keys_to_clear = [ | |
| "construction_action", | |
| "construction_rerun_pending", | |
| "construction_form_state", | |
| "construction_editor", | |
| "rerun_trigger" | |
| ] | |
| for key in keys_to_clear: | |
| if key in st.session_state: | |
| st.session_state.pop(key, None) | |
| # Clear component-specific states when leaving the Building Components page | |
| if previous_page == "Building Components": | |
| keys_to_clear = [ | |
| "walls_editor", | |
| "roofs_editor", | |
| "floors_editor", | |
| "windows_editor", | |
| "skylights_editor", | |
| "walls_action", | |
| "roofs_action", | |
| "floors_action", | |
| "windows_action", | |
| "skylights_action", | |
| "components_rerun_pending" | |
| ] | |
| for key in keys_to_clear: | |
| if key in st.session_state: | |
| st.session_state.pop(key, None) | |
| # Clear HVAC-specific states when leaving the HVAC Loads page | |
| if previous_page == "HVAC Loads": | |
| keys_to_clear = [ | |
| "hvac_loads_rerun_pending", | |
| "hvac_loads_form_state", | |
| "hvac_loads_editor", | |
| "rerun_trigger", | |
| "hvac_country", | |
| "hvac_city", | |
| "hvac_state_province", | |
| "hvac_latitude", | |
| "hvac_longitude", | |
| "hvac_elevation", | |
| "hvac_timezone", | |
| "hvac_ground_reflectivity", | |
| "hvac_sim_type", | |
| "hvac_start_date", | |
| "hvac_end_date", | |
| "hvac_base_temp", | |
| "hvac_indoor_type", | |
| "hvac_cooling_temp", | |
| "hvac_cooling_rh", | |
| "hvac_heating_temp", | |
| "hvac_heating_rh", | |
| "adaptive_acceptability", | |
| "hvac_air_velocity", | |
| "hvac_lighting_convective", | |
| "hvac_lighting_radiative", | |
| "hvac_equipment_convective", | |
| "hvac_equipment_radiative", | |
| "ground_temp_depth" | |
| ] + [f"ground_temp_{depth}_{month}" for depth in ["0.5", "2", "4"] for month in ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]] | |
| for key in keys_to_clear: | |
| if key in st.session_state: | |
| st.session_state.pop(key, None) | |
| # Clear any module-specific rerun flags when changing pages | |
| st.session_state.module_rerun_flags = {} | |
| def _handle_module_reruns(self): | |
| """ | |
| Handle module-specific rerun requests. | |
| This allows modules to request reruns without interfering with each other. | |
| """ | |
| # Check for any module-specific rerun requests | |
| for module_name, should_rerun in st.session_state.module_rerun_flags.items(): | |
| if should_rerun: | |
| # Clear the flag and trigger rerun | |
| st.session_state.module_rerun_flags[module_name] = False | |
| st.rerun() | |
| break # Only handle one rerun at a time | |
| def display_page(self, page: str): | |
| """ | |
| Display the content for the selected page by calling the appropriate module function. | |
| Args: | |
| page: The name of the page to display | |
| """ | |
| if page == "Intro": | |
| display_intro_page() | |
| elif page == "Building Information": | |
| display_building_info_page() | |
| elif page == "Climate Data": | |
| display_climate_page() | |
| elif page == "Material Library": | |
| display_materials_page() | |
| elif page == "Construction": | |
| display_construction_page() | |
| elif page == "Building Components": | |
| display_components_page() | |
| elif page == "Internal Loads": | |
| display_internal_loads_page() | |
| elif page == "HVAC Loads": | |
| display_hvac_loads_page() | |
| elif page == "Building Energy": | |
| display_building_energy_page() | |
| elif page == "Renewable Energy": | |
| display_renewable_energy_page() | |
| elif page == "Embodied Energy": | |
| display_embodied_energy_page() | |
| elif page == "Materials Cost": | |
| display_materials_cost_page() | |
| else: | |
| st.error(f"Unknown page: {page}") | |
| # Main entry point | |
| if __name__ == "__main__": | |
| try: | |
| app = BuildSustain() | |
| except Exception as e: | |
| st.error(f"An error occurred: {str(e)}") | |
| logger.exception("Application error") | |
| sys.path.append(os.path.join(os.path.dirname(__file__), 'data')) |