Spaces:
Sleeping
Sleeping
| """ | |
| BuildSustain - Introduction Module | |
| This module handles the introduction page of the BuildSustain application, | |
| including application information, instructions, and project management functionality | |
| (new project, import, export). | |
| Developed by: Dr Majed Abuseif, Deakin University | |
| © 2025 | |
| """ | |
| import streamlit as st | |
| import json | |
| import base64 | |
| import io | |
| import logging | |
| from datetime import datetime | |
| from typing import Dict, Any, Optional | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') | |
| logger = logging.getLogger(__name__) | |
| def display_intro_page(): | |
| """ | |
| Display the introduction page with app information, instructions, and project management options. | |
| This is the main function called by main.py when the Intro page is selected. | |
| """ | |
| st.title("BuildSustain") | |
| # Create tabs for different sections of the intro page | |
| tab1, tab2, tab3 = st.tabs(["About", "Instructions", "Project Management"]) | |
| # About tab content | |
| with tab1: | |
| display_about_section() | |
| # Instructions tab content | |
| with tab2: | |
| display_instructions_section() | |
| # Project Management tab content | |
| with tab3: | |
| display_project_management_section() | |
| def display_about_section(): | |
| """Display information about the application, its purpose, and developers.""" | |
| st.header("About BuildSustain") | |
| st.markdown(""" | |
| ### Overview | |
| BuildSustain is a web-based tool developed by Dr. Majed Abuseif at Deakin University’s School of | |
| Architecture and Built Environment. It enables students and professionals to perform energy | |
| simulations, calculate heating and cooling loads using ASHRAE-standard methods, and analyze | |
| embodied energy and material costs for sustainable building design. | |
| ### Purpose | |
| Designed as a learning platform, BuildSustain helps users understand building physics, apply | |
| industry-standard calculations, and explore sustainable design strategies. It bridges theoretical | |
| concepts with practical applications, preparing users for careers in architecture, engineering, | |
| and sustainability. | |
| ### Key Features | |
| - **Energy Simulations**: Calculate HVAC loads using ASHRAE Transfer Function Method (TFM) and | |
| Conduction Transfer Function (CTF). | |
| - **Sustainability Analysis**: Estimate embodied carbon, renewable energy options, and material costs. | |
| - **Climate Resilience**: Model future climate scenarios with RCP projections. | |
| - **User-Friendly Interface**: Intuitive navigation for students to experiment with design parameters. | |
| ### Version | |
| Beta Version 0.4.1 (2025) | |
| For detailed guidance, refer to the [BuildSustain User Guide](BuildSustain_Student_User_Guide.markdown). | |
| """) | |
| def display_instructions_section(): | |
| """Display instructions on how to use the application.""" | |
| st.header("Instructions") | |
| st.markdown(""" | |
| ### Getting Started | |
| BuildSustain is a step-by-step tool for sustainable building design. Follow the sidebar navigation | |
| to progress through the modules in order: | |
| 1. **Start a Project**: Use the Project Management tab to create a new project or import an existing one. | |
| 2. **Enter Building Information**: Define project name, building type, dimensions, and indoor conditions. | |
| 3. **Manage Climate Data**: Upload an EPW file or select a future climate projection. | |
| 4. **Define Materials and Fenestrations**: Use or customize material and window properties. | |
| 5. **Create Constructions**: Build multi-layer assemblies for walls, roofs, and floors. | |
| 6. **Add Building Components**: Specify envelope elements like walls, windows, and skylights. | |
| 7. **Set Internal Loads**: Define occupant, lighting, and equipment heat gains with schedules. | |
| 8. **Calculate HVAC Loads**: Run simulations to analyze heating and cooling requirements. | |
| ### Navigation | |
| - Use the sidebar to access modules (e.g., Building Information, HVAC Loads). | |
| - Follow “Back” and “Continue” buttons to move sequentially. | |
| - Save progress using the Project Management tab (export as JSON). | |
| ### Learning Tips | |
| - Experiment with inputs (e.g., insulation thickness, window SHGC) to see their impact on energy loads. | |
| - Review the [BuildSustain User Guide](BuildSustain_Student_User_Guide.markdown) for detailed | |
| steps and equations. | |
| - Use the ASHRAE Handbook link in the sidebar for reference. | |
| ### Key Methods | |
| BuildSustain uses ASHRAE-approved methods: | |
| - **Conduction Transfer Function (CTF)**: For heat transfer through opaque surfaces. | |
| - **Transfer Function Method (TFM)**: For HVAC load calculations. | |
| - **Solar Heat Gain**: For dynamic fenestration analysis. | |
| - **Adaptive Comfort**: For energy-efficient indoor condition settings. | |
| """) | |
| def display_project_management_section(): | |
| """Display project management options (new, import, export).""" | |
| st.header("Project Management") | |
| col1, col2, col3 = st.columns(3) | |
| # Start New Project button | |
| with col1: | |
| if st.button("Start New Project", key="start_new_project"): | |
| start_new_project() | |
| st.success("New project initialized. Navigate to Building Information to begin.") | |
| # Import Project section | |
| with col2: | |
| uploaded_file = st.file_uploader("Import Project", type=["json"], key="import_project") | |
| if uploaded_file is not None: | |
| if load_project(uploaded_file): | |
| st.success("Project imported successfully!") | |
| else: | |
| st.error("Failed to import project. The file may be corrupted or in an incorrect format.") | |
| # Export Project button | |
| with col3: | |
| if st.button("Export Project", key="export_project"): | |
| if st.session_state.project_data.get("project_name"): | |
| export_project() | |
| else: | |
| st.warning("Please enter a project name in the Building Information section before exporting.") | |
| def start_new_project(): | |
| """Initialize a new project by resetting the project_data in session state.""" | |
| # Keep a backup of the current project data in case user wants to recover | |
| if 'project_data_backup' not in st.session_state: | |
| st.session_state.project_data_backup = {} | |
| # Only backup if there's actual project data | |
| if st.session_state.project_data.get("project_name"): | |
| backup_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| st.session_state.project_data_backup[backup_timestamp] = st.session_state.project_data.copy() | |
| # Reset project data to default values | |
| st.session_state.project_data = { | |
| "project_name": "", | |
| "building_info": { | |
| "project_name": "", | |
| "floor_area": 100.0, | |
| "building_height": 3.0, | |
| "indoor_design_temp": 24.0, | |
| "indoor_design_rh": 50.0, | |
| "ventilation_rate": 0.1, | |
| "orientation_angle": 0.0, | |
| "operation_hours": 8, | |
| "building_type": "Office" | |
| }, | |
| "climate_data": {}, | |
| "materials": { | |
| "library": {}, | |
| "project": {} | |
| }, | |
| "fenestrations": { | |
| "library": {}, | |
| "project": {} | |
| }, | |
| "constructions": { | |
| "library": {}, | |
| "project": {} | |
| }, | |
| "components": { | |
| "walls": [], | |
| "roofs": [], | |
| "floors": [], | |
| "windows": [], | |
| "doors": [], | |
| "skylights": [] | |
| }, | |
| "internal_loads": { | |
| "people": [], | |
| "lighting": {}, | |
| "equipment": {} | |
| }, | |
| "hvac_loads": { | |
| "cooling": { | |
| "hourly": [], | |
| "peak": 0, | |
| "summary_tables": {}, | |
| "charts": {} | |
| }, | |
| "heating": { | |
| "hourly": [], | |
| "peak": 0, | |
| "summary_tables": {}, | |
| "charts": {} | |
| } | |
| }, | |
| "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": {} | |
| }, | |
| "materials_cost": { | |
| "total_cost_usd": 0, | |
| "breakdown_by_component": {}, | |
| "charts": {} | |
| } | |
| } | |
| logger.info("New project initialized") | |
| def load_project(uploaded_file) -> bool: | |
| """ | |
| Load a project from an uploaded JSON file. | |
| Args: | |
| uploaded_file: The uploaded file object from st.file_uploader | |
| Returns: | |
| bool: True if project was loaded successfully, False otherwise | |
| """ | |
| try: | |
| # Read the uploaded file | |
| content = uploaded_file.read() | |
| project_data = json.loads(content) | |
| # Validate the project data structure | |
| if not validate_project_data(project_data): | |
| logger.error("Invalid project data structure") | |
| return False | |
| # Keep a backup of the current project data | |
| if 'project_data_backup' not in st.session_state: | |
| st.session_state.project_data_backup = {} | |
| # Only backup if there's actual project data | |
| if st.session_state.project_data.get("project_name"): | |
| backup_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| st.session_state.project_data_backup[backup_timestamp] = st.session_state.project_data.copy() | |
| # Update session state with the loaded project data | |
| st.session_state.project_data = project_data | |
| logger.info(f"Project '{project_data.get('project_name', 'Unnamed')}' loaded successfully") | |
| return True | |
| except Exception as e: | |
| logger.error(f"Error loading project: {str(e)}") | |
| return False | |
| def validate_project_data(project_data: Dict[str, Any]) -> bool: | |
| """ | |
| Validate the structure of the project data. | |
| Args: | |
| project_data: The project data dictionary to validate | |
| Returns: | |
| bool: True if the project data structure is valid, False otherwise | |
| """ | |
| # Check for required top-level keys | |
| required_keys = [ | |
| "building_info", | |
| "climate_data", | |
| "materials", | |
| "fenestrations", | |
| "constructions", | |
| "components", | |
| "internal_loads", | |
| "hvac_loads", | |
| "building_energy", | |
| "renewable_energy", | |
| "embodied_energy", | |
| "materials_cost" | |
| ] | |
| for key in required_keys: | |
| if key not in project_data: | |
| logger.error(f"Missing required key in project data: {key}") | |
| return False | |
| # Check building_info structure | |
| building_info_keys = [ | |
| "project_name", | |
| "floor_area", | |
| "building_height", | |
| "indoor_design_temp", | |
| "indoor_design_rh", | |
| "orientation_angle", | |
| "operation_hours", | |
| "building_type" | |
| ] | |
| for key in building_info_keys: | |
| if key not in project_data["building_info"]: | |
| logger.error(f"Missing required key in building_info: {key}") | |
| return False | |
| # Check components structure | |
| component_types = ["walls", "roofs", "floors", "windows", "doors", "skylights"] | |
| for component_type in component_types: | |
| if component_type not in project_data["components"]: | |
| logger.error(f"Missing component type in components: {component_type}") | |
| return False | |
| # Check hvac_loads structure | |
| for load_type in ["cooling", "heating"]: | |
| if load_type not in project_data["hvac_loads"]: | |
| logger.error(f"Missing load type in hvac_loads: {load_type}") | |
| return False | |
| return True | |
| def export_project(): | |
| """Export the current project as a downloadable JSON file.""" | |
| try: | |
| # Convert project data to JSON | |
| project_json = json.dumps(st.session_state.project_data, indent=2) | |
| # Generate filename based on project name | |
| project_name = st.session_state.project_data.get("project_name", "unnamed_project") | |
| safe_project_name = "".join(c if c.isalnum() else "_" for c in project_name) | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filename = f"{safe_project_name}_{timestamp}.json" | |
| # Create a download button for the JSON file | |
| st.download_button( | |
| label="Download Project File", | |
| data=project_json, | |
| file_name=filename, | |
| mime="application/json", | |
| key="download_project" | |
| ) | |
| logger.info(f"Project '{project_name}' exported as {filename}") | |
| except Exception as e: | |
| st.error(f"Error exporting project: {str(e)}") | |
| logger.error(f"Error exporting project: {str(e)}") | |
| # Helper function to create a download link (alternative to st.download_button) | |
| def get_download_link(data, filename, link_text): | |
| """ | |
| Generate a download link for a file. | |
| Args: | |
| data: The data to be downloaded | |
| filename: The name of the file | |
| link_text: The text to display for the download link | |
| Returns: | |
| str: HTML link for downloading the file | |
| """ | |
| b64 = base64.b64encode(data.encode()).decode() | |
| href = f'<a href="data:application/json;base64,{b64}" download="{filename}">{link_text}</a>' | |
| return href |