File size: 19,432 Bytes
9bf0764
64cb021
9bf0764
10580ce
9bf0764
 
 
 
 
 
 
 
 
 
 
 
632dde6
9bf0764
 
 
 
 
 
 
 
 
dbeb8a2
 
 
189c9ac
5abbae0
dbeb8a2
15d3b04
2aa65f6
dbeb8a2
 
 
 
06b3a3b
9bf0764
10580ce
75a775e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9bf0764
 
 
 
 
 
 
 
 
189c9ac
28d8050
 
 
 
9bf0764
 
 
 
 
 
 
 
2ba7782
d788a62
 
 
 
2ba7782
9bf0764
b558e18
 
 
 
 
 
 
5d38858
 
 
 
 
b558e18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632dde6
 
 
 
 
 
189c9ac
632dde6
 
 
189c9ac
 
b558e18
9bf0764
c9450ea
9bf0764
 
 
c9450ea
9bf0764
 
 
06b3a3b
9bf0764
 
 
 
 
 
 
 
 
 
65eca13
 
 
 
01c9136
 
9bf0764
632dde6
 
 
 
 
 
 
9bf0764
 
 
9f2e218
9bf0764
189c9ac
 
 
 
 
 
 
 
632dde6
 
 
 
189c9ac
9bf0764
 
 
9f2e218
9bf0764
189c9ac
 
 
 
 
 
 
 
 
 
 
9bf0764
189c9ac
75a775e
 
 
189c9ac
28d8050
632dde6
 
28d8050
 
189c9ac
4d8b855
28d8050
 
189c9ac
28d8050
 
9bf0764
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7759948
 
9bf0764
 
 
 
7759948
 
9bf0764
 
 
 
 
 
 
b558e18
20f6549
 
9bf0764
 
 
 
 
 
10580ce
9bf0764
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20f6549
 
 
9bf0764
 
 
 
 
 
 
 
 
 
20f6549
 
 
9bf0764
 
 
 
f7f18f5
9bf0764
 
 
 
 
 
 
 
3a50f09
 
9bf0764
 
 
 
 
 
 
 
 
 
b558e18
20f6549
 
 
 
 
 
 
 
 
 
c9450ea
20f6549
c9450ea
 
87ad425
 
 
c9450ea
 
87ad425
 
20f6549
 
 
 
 
b558e18
 
 
 
 
 
 
 
 
 
 
 
 
 
20f6549
5abbae0
 
 
 
 
 
 
 
 
 
 
 
 
fb4989f
f344d5e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189c9ac
28d8050
 
 
 
 
189c9ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632dde6
 
 
 
 
 
 
 
28d8050
 
 
 
65eca13
 
20f6549
 
 
 
 
 
 
 
fb4989f
 
 
 
 
9bf0764
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10580ce
9bf0764
 
 
 
75a775e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
"""
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'))