BuildSustain-02 / app /building_information.py
mabuseif's picture
Update app/building_information.py
cc05fa7 verified
"""
BuildSustain - Building Information Module
This module handles the building information input page of the BuildSustain application,
allowing users to define basic building parameters such as project name, floor area, building height,
indoor design conditions for winter and summer, orientation, and building type.
Developed by: Dr Majed Abuseif, Deakin University
© 2025
"""
import streamlit as st
import logging
import math
from typing import Dict, Any, Optional, List, Tuple
from app.i_l_data import DEFAULT_BUILDING_INTERNALS
# Define BUILDING_TYPES as the list of keys from DEFAULT_BUILDING_INTERNALS
BUILDING_TYPES = list(DEFAULT_BUILDING_INTERNALS.keys())
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def display_building_info_page():
"""
Display the building information input page.
This is the main function called by main.py when the Building Information page is selected.
"""
st.title("Building Information")
# Display help information in an expandable section
with st.expander("Help & Information"):
display_building_info_help()
# Create the main form for building information input
with st.form("building_info_form"):
# Get current values from session state or use defaults
current_values = st.session_state.project_data["building_info"]
# Project Name and other fields
col1, col2 = st.columns(2)
with col1:
project_name = st.text_input(
"Project Name",
value=current_values["project_name"],
help="Enter a unique identifier for your project."
)
# Building Type
building_type = st.selectbox(
"Building Type",
options=BUILDING_TYPES,
index=BUILDING_TYPES.index(current_values["building_type"]) if current_values["building_type"] in BUILDING_TYPES else 0,
help="Primary use of the building, which affects default internal loads."
)
# Floor Area
floor_area = st.number_input(
"Floor Area (square meters)",
min_value=1.0,
max_value=100000.0,
value=float(current_values["floor_area"]),
step=10.0,
format="%.1f",
help="Total conditioned floor area of the building in square meters."
)
# Building Height
building_height = st.number_input(
"Building Height (meters)",
min_value=2.0,
max_value=100.0,
value=float(current_values["building_height"]),
step=0.1,
format="%.1f",
help="Average ceiling height of the building in meters."
)
with col2:
# Summer Indoor Design Temperature
summer_indoor_design_temp = st.number_input(
"Summer Indoor Design Temperature (°C)",
min_value=15.0,
max_value=30.0,
value=float(current_values.get("summer_indoor_design_temp", current_values.get("indoor_design_temp", 24.0))),
step=0.5,
format="%.1f",
help="Target indoor temperature for summer season comfort (15–30°C)."
)
# Winter Indoor Design Temperature
winter_indoor_design_temp = st.number_input(
"Winter Indoor Design Temperature (°C)",
min_value=15.0,
max_value=30.0,
value=float(current_values.get("winter_indoor_design_temp", current_values.get("indoor_design_temp", 20.0))),
step=0.5,
format="%.1f",
help="Target indoor temperature for winter season comfort (15–30°C)."
)
# Summer Indoor Design Relative Humidity
summer_indoor_design_rh = st.number_input(
"Summer Indoor Design Relative Humidity (%)",
min_value=20.0,
max_value=80.0,
value=float(current_values.get("summer_indoor_design_rh", current_values.get("indoor_design_rh", 50.0))),
step=5.0,
format="%.1f",
help="Target indoor relative humidity for summer season comfort (20–80%)."
)
# Winter Indoor Design Relative Humidity
winter_indoor_design_rh = st.number_input(
"Winter Indoor Design Relative Humidity (%)",
min_value=20.0,
max_value=80.0,
value=float(current_values.get("winter_indoor_design_rh", current_values.get("indoor_design_rh", 50.0))),
step=5.0,
format="%.1f",
help="Target indoor relative humidity for winter season comfort (20–80%)."
)
# Building Orientation
st.subheader("Building Orientation")
# Display orientation images
display_orientation_diagram()
orientation_angle = st.slider(
"Orientation Angle (°)",
min_value=-180,
max_value=180,
value=int(current_values["orientation_angle"]),
step=5,
help="Sets the building's rotation angle relative to north (0°). Component orientations are defined as A (North), B (South), C (East), D (West) at 0°. Adjusting this angle rotates all component orientations accordingly."
)
# Form submission button
submit_button = st.form_submit_button("Save Building Information")
if submit_button:
# Validate inputs
validation_errors = validate_building_info(
project_name, floor_area, building_height, building_type,
winter_indoor_design_temp, winter_indoor_design_rh,
summer_indoor_design_temp, summer_indoor_design_rh,
orientation_angle
)
if validation_errors:
# Display validation errors
for error in validation_errors:
st.error(error)
else:
# Update session state with validated inputs
st.session_state.project_data["project_name"] = project_name
st.session_state.project_data["building_info"].update({
"project_name": project_name,
"floor_area": floor_area,
"building_height": building_height,
"building_type": building_type,
"winter_indoor_design_temp": winter_indoor_design_temp,
"winter_indoor_design_rh": winter_indoor_design_rh,
"summer_indoor_design_temp": summer_indoor_design_temp,
"summer_indoor_design_rh": summer_indoor_design_rh,
"orientation_angle": float(orientation_angle)
})
# Log the update
logger.info(f"Building information updated for project: {project_name}")
# Show success message
st.success("Building information saved successfully!")
# Navigation buttons
col1, col2 = st.columns(2)
with col1:
if st.button("Back to Intro", key="back_to_intro"):
st.session_state.current_page = "Intro"
st.rerun()
with col2:
if st.button("Continue to Climate Data", key="continue_to_climate"):
# Check if required fields are filled before proceeding
if not st.session_state.project_data["building_info"]["project_name"]:
st.error("Please enter a project name before continuing.")
else:
st.session_state.current_page = "Climate Data"
st.rerun()
def validate_building_info(
project_name: str,
floor_area: float,
building_height: float,
building_type: str,
winter_indoor_design_temp: float,
winter_indoor_design_rh: float,
summer_indoor_design_temp: float,
summer_indoor_design_rh: float,
orientation_angle: int
) -> List[str]:
"""
Validate building information inputs.
Args:
project_name: Project name
floor_area: Floor area in square meters
building_height: Building height in meters
building_type: Building type
winter_indoor_design_temp: Winter indoor design temperature in °C
winter_indoor_design_rh: Winter indoor design relative humidity in %
summer_indoor_design_temp: Summer indoor design temperature in °C
summer_indoor_design_rh: Summer indoor design relative humidity in %
orientation_angle: Building orientation angle in degrees
Returns:
List of validation error messages, empty if all inputs are valid
"""
errors = []
# Validate project name
if not project_name or project_name.strip() == "":
errors.append("Project name is required.")
# Validate floor area
if floor_area <= 0:
errors.append("Floor area must be greater than zero.")
elif floor_area > 100000:
errors.append("Floor area exceeds maximum value (100,000 m²).")
# Validate building height
if building_height < 2.0:
errors.append("Building height must be at least 2.0 meters.")
elif building_height > 100.0:
errors.append("Building height exceeds maximum value (100 meters).")
# Validate building type
if building_type not in BUILDING_TYPES:
errors.append("Please select a valid building type.")
# Validate winter indoor design temperature
if winter_indoor_design_temp < 15.0 or winter_indoor_design_temp > 30.0:
errors.append("Winter indoor design temperature must be between 15°C and 30°C.")
# Validate winter indoor design relative humidity
if winter_indoor_design_rh < 20.0 or winter_indoor_design_rh > 80.0:
errors.append("Winter indoor design relative humidity must be between 20% and 80%.")
# Validate summer indoor design temperature
if summer_indoor_design_temp < 15.0 or summer_indoor_design_temp > 30.0:
errors.append("Summer indoor design temperature must be between 15°C and 30°C.")
# Validate summer indoor design relative humidity
if summer_indoor_design_rh < 20.0 or summer_indoor_design_rh > 80.0:
errors.append("Summer indoor design relative humidity must be between 20% and 80%.")
# Validate orientation angle
if orientation_angle < -180 or orientation_angle > 180:
errors.append("Orientation angle must be between -180° and 180°.")
return errors
def display_building_info_help():
"""Display help information for the building information page."""
st.markdown("""
### Building Information Help
This section collects basic information about your building project. The inputs provided here will be used throughout the calculation process.
**Key Parameters:**
* **Project Name**: A unique identifier for your project.
* **Floor Area**: The total conditioned floor area of the building in square meters.
* **Building Height**: The average ceiling height of the building in meters.
* **Building Type**: The primary use of the building, which affects default internal loads.
* **Winter Indoor Design Temperature**: The target indoor temperature for winter season comfort (typically 20-24°C).
* **Winter Indoor Design Relative Humidity**: The target indoor relative humidity for winter season comfort (typically 30-50%).
* **Summer Indoor Design Temperature**: The target indoor temperature for summer season comfort (typically 22-26°C).
* **Summer Indoor Design Relative Humidity**: The target indoor relative humidity for summer season comfort (typically 40-60%).
* **Orientation Angle**: The building's rotation angle relative to north (0°).
**Building Orientation:**
The orientation angle rotates the entire building relative to true north. At 0°, the building facades are aligned with the cardinal directions:
* Facade A: North (0°)
* Facade B: South (180°)
* Facade C: East (90°)
* Facade D: West (270°)
Adjusting the orientation angle will rotate all facades accordingly.
""")
def display_orientation_diagram():
"""Display orientation images showing building orientation."""
col1, col2 = st.columns(2)
with col1:
st.markdown(
'<div style="display: flex; justify-content: center; align-items: center; width: 100%; text-align: center; margin: 0 auto;">',
unsafe_allow_html=True
)
st.image("images/orientation1.jpg", caption="Building Orientation Angle = 0", width=300, use_column_width=False, output_format="auto")
st.markdown('</div>', unsafe_allow_html=True)
with col2:
st.markdown(
'<div style="display: flex; justify-content: center; align-items: center; width: 100%; text-align: center; margin: 0 auto;">',
unsafe_allow_html=True
)
st.image("images/orientation2.jpg", caption="Building Orientation Angle = 45", width=300, use_column_width=False, output_format="auto")
st.markdown('</div>', unsafe_allow_html=True)
def calculate_volume(floor_area: float, height: float) -> float:
"""
Calculate the building volume.
Args:
floor_area: Floor area in square meters
height: Building height in meters
Returns:
Building volume in cubic meters
"""
return floor_area * height