BuildSustain-02 / app /intro.py
mabuseif's picture
Update app/intro.py
1dfc844 verified
"""
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