Spaces:
Sleeping
Sleeping
Update app/intro.py
Browse files- app/intro.py +77 -203
app/intro.py
CHANGED
@@ -16,7 +16,6 @@ import io
|
|
16 |
import logging
|
17 |
from datetime import datetime
|
18 |
from typing import Dict, Any, Optional
|
19 |
-
from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, SAMPLE_CONSTRUCTIONS
|
20 |
|
21 |
# Configure logging
|
22 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
@@ -51,31 +50,36 @@ def display_about_section():
|
|
51 |
st.markdown("""
|
52 |
### Overview
|
53 |
|
54 |
-
BuildSustain is a
|
55 |
-
|
|
|
|
|
56 |
|
57 |
-
###
|
58 |
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
70 |
|
71 |
### Development
|
72 |
|
73 |
-
|
74 |
-
|
75 |
|
76 |
### Version
|
77 |
|
78 |
Beta Version 0.4.1 (2025)
|
|
|
|
|
79 |
""")
|
80 |
|
81 |
def display_instructions_section():
|
@@ -85,33 +89,38 @@ def display_instructions_section():
|
|
85 |
st.markdown("""
|
86 |
### Getting Started
|
87 |
|
88 |
-
|
89 |
-
|
90 |
-
3. **Define Materials**: Use the library or create custom materials
|
91 |
-
4. **Create Constructions**: Define wall, roof, and floor assemblies
|
92 |
-
5. **Add Building Components**: Specify the building envelope components
|
93 |
-
6. **Define Internal Loads**: Add occupancy, lighting, and equipment
|
94 |
-
7. **Calculate Loads**: Generate cooling and heating load results
|
95 |
-
8. **Explore Additional Analyses**: Energy consumption, renewable energy, embodied carbon, and cost
|
96 |
|
97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
|
99 |
-
|
100 |
-
sections at any time to modify inputs.
|
101 |
|
102 |
-
|
|
|
|
|
103 |
|
104 |
-
|
105 |
-
continue your work.
|
106 |
|
107 |
-
|
|
|
|
|
|
|
108 |
|
109 |
-
|
110 |
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
|
|
115 |
""")
|
116 |
|
117 |
def display_project_management_section():
|
@@ -144,7 +153,7 @@ def display_project_management_section():
|
|
144 |
st.warning("Please enter a project name in the Building Information section before exporting.")
|
145 |
|
146 |
def start_new_project():
|
147 |
-
"""Initialize a new project by resetting
|
148 |
# Keep a backup of the current project data in case user wants to recover
|
149 |
if 'project_data_backup' not in st.session_state:
|
150 |
st.session_state.project_data_backup = {}
|
@@ -154,78 +163,31 @@ def start_new_project():
|
|
154 |
backup_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
155 |
st.session_state.project_data_backup[backup_timestamp] = st.session_state.project_data.copy()
|
156 |
|
157 |
-
#
|
158 |
-
library_data = {
|
159 |
-
"materials": st.session_state.project_data.get("materials", {}).get("library", dict(SAMPLE_MATERIALS)),
|
160 |
-
"fenestrations": st.session_state.project_data.get("fenestrations", {}).get("library", dict(SAMPLE_FENESTRATIONS)),
|
161 |
-
"constructions": st.session_state.project_data.get("constructions", {}).get("library", dict(SAMPLE_CONSTRUCTIONS))
|
162 |
-
}
|
163 |
-
|
164 |
-
# Reset project data to match main.py structure
|
165 |
st.session_state.project_data = {
|
166 |
"project_name": "",
|
167 |
"building_info": {
|
168 |
"project_name": "",
|
169 |
"floor_area": 100.0,
|
170 |
"building_height": 3.0,
|
171 |
-
"
|
172 |
-
"
|
173 |
-
"
|
174 |
-
"
|
175 |
-
"
|
176 |
-
"
|
177 |
-
},
|
178 |
-
"climate_data": {
|
179 |
-
"id": "",
|
180 |
-
"location": {
|
181 |
-
"city": "",
|
182 |
-
"state_province": "",
|
183 |
-
"country": "",
|
184 |
-
"source": "",
|
185 |
-
"wmo": "",
|
186 |
-
"latitude": 0.0,
|
187 |
-
"longitude": 0.0,
|
188 |
-
"timezone": 0.0,
|
189 |
-
"elevation": 0.0
|
190 |
-
},
|
191 |
-
"design_conditions": {
|
192 |
-
"winter_design_temp": 0.0,
|
193 |
-
"summer_design_temp_db": 30.0,
|
194 |
-
"summer_design_temp_wb": 25.0,
|
195 |
-
"heating_degree_days": 0,
|
196 |
-
"cooling_degree_days": 0,
|
197 |
-
"monthly_average_temps": [20.0] * 12,
|
198 |
-
"monthly_average_radiation": [150.0] * 12,
|
199 |
-
"summer_daily_range": 8.0,
|
200 |
-
"wind_speed": 3.0,
|
201 |
-
"pressure": 101325.0
|
202 |
-
},
|
203 |
-
"climate_zone": "",
|
204 |
-
"hourly_data": [],
|
205 |
-
"epw_filename": "",
|
206 |
-
"typical_extreme_periods": {
|
207 |
-
"summer_extreme": {"start": {"month": 7, "day": 1}, "end": {"month": 7, "day": 7}},
|
208 |
-
"summer_typical": {"start": {"month": 6, "day": 1}, "end": {"month": 6, "day": 7}},
|
209 |
-
"winter_extreme": {"start": {"month": 1, "day": 1}, "end": {"month": 1, "day": 7}},
|
210 |
-
"winter_typical": {"start": {"month": 12, "day": 1}, "end": {"month": 12, "day": 7}}
|
211 |
-
},
|
212 |
-
"ground_temperatures": {
|
213 |
-
"0.5": [20.0] * 12,
|
214 |
-
"2": [18.0] * 12,
|
215 |
-
"4": [16.0] * 12
|
216 |
-
},
|
217 |
-
"ground_reflectivity": 0.2
|
218 |
},
|
|
|
219 |
"materials": {
|
220 |
-
"library":
|
221 |
"project": {}
|
222 |
},
|
223 |
"fenestrations": {
|
224 |
-
"library":
|
225 |
"project": {}
|
226 |
},
|
227 |
"constructions": {
|
228 |
-
"library":
|
229 |
"project": {}
|
230 |
},
|
231 |
"components": {
|
@@ -233,74 +195,27 @@ def start_new_project():
|
|
233 |
"roofs": [],
|
234 |
"floors": [],
|
235 |
"windows": [],
|
|
|
236 |
"skylights": []
|
237 |
},
|
238 |
"internal_loads": {
|
239 |
-
"schedules": {},
|
240 |
"people": [],
|
241 |
-
"lighting":
|
242 |
-
"equipment":
|
243 |
-
"ventilation": [],
|
244 |
-
"infiltration": []
|
245 |
-
},
|
246 |
-
"internal_loads_conditions": {
|
247 |
-
"air_velocity": 0.1,
|
248 |
-
"lighting_convective_fraction": 0.5,
|
249 |
-
"lighting_radiative_fraction": 0.5,
|
250 |
-
"equipment_convective_fraction": 0.5,
|
251 |
-
"equipment_radiative_fraction": 0.5
|
252 |
},
|
253 |
"hvac_loads": {
|
254 |
"cooling": {
|
255 |
"hourly": [],
|
256 |
-
"peak": 0
|
257 |
"summary_tables": {},
|
258 |
-
"charts": {
|
259 |
-
"pie_by_component": {},
|
260 |
-
"pie_by_orientation": {}
|
261 |
-
},
|
262 |
-
"breakdown": {
|
263 |
-
"conduction": 0.0,
|
264 |
-
"solar": 0.0,
|
265 |
-
"internal": 0.0,
|
266 |
-
"ventilation_sensible": 0.0,
|
267 |
-
"ventilation_latent": 0.0,
|
268 |
-
"infiltration_sensible": 0.0,
|
269 |
-
"infiltration_latent": 0.0
|
270 |
-
}
|
271 |
},
|
272 |
"heating": {
|
273 |
"hourly": [],
|
274 |
-
"peak": 0
|
275 |
"summary_tables": {},
|
276 |
-
"charts": {
|
277 |
-
|
278 |
-
"pie_by_orientation": {}
|
279 |
-
},
|
280 |
-
"breakdown": {
|
281 |
-
"conduction": 0.0,
|
282 |
-
"ventilation": 0.0,
|
283 |
-
"infiltration": 0.0
|
284 |
-
}
|
285 |
-
},
|
286 |
-
"monthly_summary": {}
|
287 |
-
},
|
288 |
-
"hvac_settings": {
|
289 |
-
"operating_hours": [{"start": 8, "end": 18}],
|
290 |
-
"system_type": "Default"
|
291 |
-
},
|
292 |
-
"sim_period": {
|
293 |
-
"type": "Full Year",
|
294 |
-
"start_date": datetime(2025, 1, 1),
|
295 |
-
"end_date": datetime(2025, 12, 31),
|
296 |
-
"base_temp": 18.3
|
297 |
-
},
|
298 |
-
"indoor_conditions": {
|
299 |
-
"type": "Fixed Setpoints",
|
300 |
-
"cooling_setpoint": {"temperature": 24.0, "rh": 50.0},
|
301 |
-
"heating_setpoint": {"temperature": 20.0, "rh": 50.0},
|
302 |
-
"adaptive_acceptability": "90",
|
303 |
-
"schedule": []
|
304 |
},
|
305 |
"building_energy": {
|
306 |
"hvac_type": "",
|
@@ -324,18 +239,16 @@ def start_new_project():
|
|
324 |
"embodied_energy": {
|
325 |
"total_embodied_carbon_kgco2e": 0,
|
326 |
"breakdown_by_component": {},
|
327 |
-
"charts": {}
|
328 |
-
"material_inventory": []
|
329 |
},
|
330 |
"materials_cost": {
|
331 |
"total_cost_usd": 0,
|
332 |
"breakdown_by_component": {},
|
333 |
-
"charts": {}
|
334 |
-
"material_inventory": []
|
335 |
}
|
336 |
}
|
337 |
|
338 |
-
logger.info("New project initialized
|
339 |
|
340 |
def load_project(uploaded_file) -> bool:
|
341 |
"""
|
@@ -366,12 +279,6 @@ def load_project(uploaded_file) -> bool:
|
|
366 |
backup_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
367 |
st.session_state.project_data_backup[backup_timestamp] = st.session_state.project_data.copy()
|
368 |
|
369 |
-
# Convert datetime strings back to datetime objects if necessary
|
370 |
-
if project_data.get("sim_period", {}).get("start_date"):
|
371 |
-
project_data["sim_period"]["start_date"] = datetime.fromisoformat(project_data["sim_period"]["start_date"])
|
372 |
-
if project_data.get("sim_period", {}).get("end_date"):
|
373 |
-
project_data["sim_period"]["end_date"] = datetime.fromisoformat(project_data["sim_period"]["end_date"])
|
374 |
-
|
375 |
# Update session state with the loaded project data
|
376 |
st.session_state.project_data = project_data
|
377 |
|
@@ -394,7 +301,6 @@ def validate_project_data(project_data: Dict[str, Any]) -> bool:
|
|
394 |
"""
|
395 |
# Check for required top-level keys
|
396 |
required_keys = [
|
397 |
-
"project_name",
|
398 |
"building_info",
|
399 |
"climate_data",
|
400 |
"materials",
|
@@ -402,11 +308,7 @@ def validate_project_data(project_data: Dict[str, Any]) -> bool:
|
|
402 |
"constructions",
|
403 |
"components",
|
404 |
"internal_loads",
|
405 |
-
"internal_loads_conditions",
|
406 |
"hvac_loads",
|
407 |
-
"hvac_settings",
|
408 |
-
"sim_period",
|
409 |
-
"indoor_conditions",
|
410 |
"building_energy",
|
411 |
"renewable_energy",
|
412 |
"embodied_energy",
|
@@ -423,12 +325,11 @@ def validate_project_data(project_data: Dict[str, Any]) -> bool:
|
|
423 |
"project_name",
|
424 |
"floor_area",
|
425 |
"building_height",
|
426 |
-
"
|
427 |
-
"
|
428 |
-
"
|
429 |
-
"
|
430 |
-
"
|
431 |
-
"orientation_angle"
|
432 |
]
|
433 |
|
434 |
for key in building_info_keys:
|
@@ -436,26 +337,8 @@ def validate_project_data(project_data: Dict[str, Any]) -> bool:
|
|
436 |
logger.error(f"Missing required key in building_info: {key}")
|
437 |
return False
|
438 |
|
439 |
-
# Check climate_data structure
|
440 |
-
climate_data_keys = [
|
441 |
-
"id",
|
442 |
-
"location",
|
443 |
-
"design_conditions",
|
444 |
-
"climate_zone",
|
445 |
-
"hourly_data",
|
446 |
-
"epw_filename",
|
447 |
-
"typical_extreme_periods",
|
448 |
-
"ground_temperatures",
|
449 |
-
"ground_reflectivity"
|
450 |
-
]
|
451 |
-
|
452 |
-
for key in climate_data_keys:
|
453 |
-
if key not in project_data["climate_data"]:
|
454 |
-
logger.error(f"Missing required key in climate_data: {key}")
|
455 |
-
return False
|
456 |
-
|
457 |
# Check components structure
|
458 |
-
component_types = ["walls", "roofs", "floors", "windows", "skylights"]
|
459 |
for component_type in component_types:
|
460 |
if component_type not in project_data["components"]:
|
461 |
logger.error(f"Missing component type in components: {component_type}")
|
@@ -472,20 +355,11 @@ def validate_project_data(project_data: Dict[str, Any]) -> bool:
|
|
472 |
def export_project():
|
473 |
"""Export the current project as a downloadable JSON file."""
|
474 |
try:
|
475 |
-
# Create a deep copy of project data to avoid modifying session state
|
476 |
-
project_data = st.session_state.project_data.copy()
|
477 |
-
|
478 |
-
# Convert datetime objects to ISO format strings for JSON serialization
|
479 |
-
if project_data.get("sim_period", {}).get("start_date"):
|
480 |
-
project_data["sim_period"]["start_date"] = project_data["sim_period"]["start_date"].isoformat()
|
481 |
-
if project_data.get("sim_period", {}).get("end_date"):
|
482 |
-
project_data["sim_period"]["end_date"] = project_data["sim_period"]["end_date"].isoformat()
|
483 |
-
|
484 |
# Convert project data to JSON
|
485 |
-
project_json = json.dumps(project_data, indent=2)
|
486 |
|
487 |
# Generate filename based on project name
|
488 |
-
project_name = project_data.get("project_name", "unnamed_project")
|
489 |
safe_project_name = "".join(c if c.isalnum() else "_" for c in project_name)
|
490 |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
491 |
filename = f"{safe_project_name}_{timestamp}.json"
|
|
|
16 |
import logging
|
17 |
from datetime import datetime
|
18 |
from typing import Dict, Any, Optional
|
|
|
19 |
|
20 |
# Configure logging
|
21 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
50 |
st.markdown("""
|
51 |
### Overview
|
52 |
|
53 |
+
BuildSustain is a web-based tool developed by Dr. Majed Abuseif at Deakin University’s School of
|
54 |
+
Architecture and Built Environment. It enables students and professionals to perform energy
|
55 |
+
simulations, calculate heating and cooling loads using ASHRAE-standard methods, and analyze
|
56 |
+
embodied energy and material costs for sustainable building design.
|
57 |
|
58 |
+
### Purpose
|
59 |
|
60 |
+
Designed as a learning platform, BuildSustain helps users understand building physics, apply
|
61 |
+
industry-standard calculations, and explore sustainable design strategies. It bridges theoretical
|
62 |
+
concepts with practical applications, preparing users for careers in architecture, engineering,
|
63 |
+
and sustainability.
|
64 |
+
|
65 |
+
### Key Features
|
66 |
+
|
67 |
+
- **Energy Simulations**: Calculate HVAC loads using ASHRAE Transfer Function Method (TFM) and
|
68 |
+
Conduction Transfer Function (CTF).
|
69 |
+
- **Sustainability Analysis**: Estimate embodied carbon, renewable energy options, and material costs.
|
70 |
+
- **Climate Resilience**: Model future climate scenarios with RCP projections.
|
71 |
+
- **User-Friendly Interface**: Intuitive navigation for students to experiment with design parameters.
|
72 |
|
73 |
### Development
|
74 |
|
75 |
+
Created by Dr. Majed Abuseif, BuildSustain is a powerful educational tool for precise sustainability
|
76 |
+
calculations, focusing on climate change resilience and energy-efficient design.
|
77 |
|
78 |
### Version
|
79 |
|
80 |
Beta Version 0.4.1 (2025)
|
81 |
+
|
82 |
+
For detailed guidance, refer to the [BuildSustain Student User Guide](BuildSustain_Student_User_Guide.markdown).
|
83 |
""")
|
84 |
|
85 |
def display_instructions_section():
|
|
|
89 |
st.markdown("""
|
90 |
### Getting Started
|
91 |
|
92 |
+
BuildSustain is a step-by-step tool for sustainable building design. Follow the sidebar navigation
|
93 |
+
to progress through the modules in order:
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
+
1. **Start a Project**: Use the Project Management tab to create a new project or import an existing one.
|
96 |
+
2. **Enter Building Information**: Define project name, building type, dimensions, and indoor conditions.
|
97 |
+
3. **Manage Climate Data**: Upload an EPW file or select a future climate projection.
|
98 |
+
4. **Define Materials and Fenestrations**: Use or customize material and window properties.
|
99 |
+
5. **Create Constructions**: Build multi-layer assemblies for walls, roofs, and floors.
|
100 |
+
6. **Add Building Components**: Specify envelope elements like walls, windows, and skylights.
|
101 |
+
7. **Set Internal Loads**: Define occupant, lighting, and equipment heat gains with schedules.
|
102 |
+
8. **Calculate HVAC Loads**: Run simulations to analyze heating and cooling requirements.
|
103 |
|
104 |
+
### Navigation
|
|
|
105 |
|
106 |
+
- Use the sidebar to access modules (e.g., Building Information, HVAC Loads).
|
107 |
+
- Follow “Back” and “Continue” buttons to move sequentially.
|
108 |
+
- Save progress using the Project Management tab (export as JSON).
|
109 |
|
110 |
+
### Learning Tips
|
|
|
111 |
|
112 |
+
- Experiment with inputs (e.g., insulation thickness, window SHGC) to see their impact on energy loads.
|
113 |
+
- Review the [BuildSustain Student User Guide](BuildSustain_Student_User_Guide.markdown) for detailed
|
114 |
+
steps and equations.
|
115 |
+
- Use the ASHRAE Handbook link in the sidebar for reference.
|
116 |
|
117 |
+
### Key Methods
|
118 |
|
119 |
+
BuildSustain uses ASHRAE-approved methods:
|
120 |
+
- **Conduction Transfer Function (CTF)**: For heat transfer through opaque surfaces.
|
121 |
+
- **Transfer Function Method (TFM)**: For HVAC load calculations.
|
122 |
+
- **Solar Heat Gain**: For dynamic fenestration analysis.
|
123 |
+
- **Adaptive Comfort**: For energy-efficient indoor condition settings.
|
124 |
""")
|
125 |
|
126 |
def display_project_management_section():
|
|
|
153 |
st.warning("Please enter a project name in the Building Information section before exporting.")
|
154 |
|
155 |
def start_new_project():
|
156 |
+
"""Initialize a new project by resetting the project_data in session state."""
|
157 |
# Keep a backup of the current project data in case user wants to recover
|
158 |
if 'project_data_backup' not in st.session_state:
|
159 |
st.session_state.project_data_backup = {}
|
|
|
163 |
backup_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
164 |
st.session_state.project_data_backup[backup_timestamp] = st.session_state.project_data.copy()
|
165 |
|
166 |
+
# Reset project data to default values
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
st.session_state.project_data = {
|
168 |
"project_name": "",
|
169 |
"building_info": {
|
170 |
"project_name": "",
|
171 |
"floor_area": 100.0,
|
172 |
"building_height": 3.0,
|
173 |
+
"indoor_design_temp": 24.0,
|
174 |
+
"indoor_design_rh": 50.0,
|
175 |
+
"ventilation_rate": 0.1,
|
176 |
+
"orientation_angle": 0.0,
|
177 |
+
"operation_hours": 8,
|
178 |
+
"building_type": "Office"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
},
|
180 |
+
"climate_data": {},
|
181 |
"materials": {
|
182 |
+
"library": {},
|
183 |
"project": {}
|
184 |
},
|
185 |
"fenestrations": {
|
186 |
+
"library": {},
|
187 |
"project": {}
|
188 |
},
|
189 |
"constructions": {
|
190 |
+
"library": {},
|
191 |
"project": {}
|
192 |
},
|
193 |
"components": {
|
|
|
195 |
"roofs": [],
|
196 |
"floors": [],
|
197 |
"windows": [],
|
198 |
+
"doors": [],
|
199 |
"skylights": []
|
200 |
},
|
201 |
"internal_loads": {
|
|
|
202 |
"people": [],
|
203 |
+
"lighting": {},
|
204 |
+
"equipment": {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
},
|
206 |
"hvac_loads": {
|
207 |
"cooling": {
|
208 |
"hourly": [],
|
209 |
+
"peak": 0,
|
210 |
"summary_tables": {},
|
211 |
+
"charts": {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
},
|
213 |
"heating": {
|
214 |
"hourly": [],
|
215 |
+
"peak": 0,
|
216 |
"summary_tables": {},
|
217 |
+
"charts": {}
|
218 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
},
|
220 |
"building_energy": {
|
221 |
"hvac_type": "",
|
|
|
239 |
"embodied_energy": {
|
240 |
"total_embodied_carbon_kgco2e": 0,
|
241 |
"breakdown_by_component": {},
|
242 |
+
"charts": {}
|
|
|
243 |
},
|
244 |
"materials_cost": {
|
245 |
"total_cost_usd": 0,
|
246 |
"breakdown_by_component": {},
|
247 |
+
"charts": {}
|
|
|
248 |
}
|
249 |
}
|
250 |
|
251 |
+
logger.info("New project initialized")
|
252 |
|
253 |
def load_project(uploaded_file) -> bool:
|
254 |
"""
|
|
|
279 |
backup_timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
280 |
st.session_state.project_data_backup[backup_timestamp] = st.session_state.project_data.copy()
|
281 |
|
|
|
|
|
|
|
|
|
|
|
|
|
282 |
# Update session state with the loaded project data
|
283 |
st.session_state.project_data = project_data
|
284 |
|
|
|
301 |
"""
|
302 |
# Check for required top-level keys
|
303 |
required_keys = [
|
|
|
304 |
"building_info",
|
305 |
"climate_data",
|
306 |
"materials",
|
|
|
308 |
"constructions",
|
309 |
"components",
|
310 |
"internal_loads",
|
|
|
311 |
"hvac_loads",
|
|
|
|
|
|
|
312 |
"building_energy",
|
313 |
"renewable_energy",
|
314 |
"embodied_energy",
|
|
|
325 |
"project_name",
|
326 |
"floor_area",
|
327 |
"building_height",
|
328 |
+
"indoor_design_temp",
|
329 |
+
"indoor_design_rh",
|
330 |
+
"orientation_angle",
|
331 |
+
"operation_hours",
|
332 |
+
"building_type"
|
|
|
333 |
]
|
334 |
|
335 |
for key in building_info_keys:
|
|
|
337 |
logger.error(f"Missing required key in building_info: {key}")
|
338 |
return False
|
339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
# Check components structure
|
341 |
+
component_types = ["walls", "roofs", "floors", "windows", "doors", "skylights"]
|
342 |
for component_type in component_types:
|
343 |
if component_type not in project_data["components"]:
|
344 |
logger.error(f"Missing component type in components: {component_type}")
|
|
|
355 |
def export_project():
|
356 |
"""Export the current project as a downloadable JSON file."""
|
357 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
358 |
# Convert project data to JSON
|
359 |
+
project_json = json.dumps(st.session_state.project_data, indent=2)
|
360 |
|
361 |
# Generate filename based on project name
|
362 |
+
project_name = st.session_state.project_data.get("project_name", "unnamed_project")
|
363 |
safe_project_name = "".join(c if c.isalnum() else "_" for c in project_name)
|
364 |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
365 |
filename = f"{safe_project_name}_{timestamp}.json"
|