Spaces:
Sleeping
Sleeping
Upload main.py
Browse files
main.py
CHANGED
|
@@ -18,7 +18,7 @@ import logging
|
|
| 18 |
from typing import Dict, List, Any, Optional, Tuple
|
| 19 |
from enum import Enum
|
| 20 |
import scipy.linalg as linalg
|
| 21 |
-
from data.material_library import MaterialLibrary, MaterialCategory, Construction, Material, GlazingMaterial
|
| 22 |
from data.climate_data import ClimateData, ClimateLocation
|
| 23 |
import streamlit.components.v1 as components
|
| 24 |
from data.calculation import TFMCalculations, ComponentType
|
|
@@ -44,6 +44,13 @@ class GlazingMaterial:
|
|
| 44 |
self.h_o = h_o
|
| 45 |
self.is_library = is_library
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
class GlazingMaterial:
|
| 48 |
def __init__(self, name: str, shgc: float, u_value: float, h_o: float, is_library: bool = True):
|
| 49 |
self.name = name
|
|
@@ -52,11 +59,18 @@ class GlazingMaterial:
|
|
| 52 |
self.h_o = h_o
|
| 53 |
self.is_library = is_library
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
class Component:
|
| 56 |
def __init__(self, name: str, component_type: ComponentType, area: float, elevation: Optional[str] = None,
|
| 57 |
orientation: Optional[float] = None, rotation: float = 0.0, tilt: float = None,
|
| 58 |
construction: Optional[Construction] = None, glazing_material: Optional[GlazingMaterial] = None,
|
| 59 |
-
door_material: Optional[
|
| 60 |
shgc: float = None):
|
| 61 |
self.name = name
|
| 62 |
self.component_type = component_type
|
|
@@ -77,8 +91,8 @@ class Component:
|
|
| 77 |
self.u_value = door_material.u_value
|
| 78 |
else:
|
| 79 |
self.u_value = construction.u_value if construction else self.calculate_u_value()
|
| 80 |
-
self.
|
| 81 |
-
construction.
|
| 82 |
self.shgc = shgc if shgc is not None else (glazing_material.shgc if glazing_material else None)
|
| 83 |
|
| 84 |
# Compute orientation_angle
|
|
@@ -480,7 +494,7 @@ class HVACCalculator:
|
|
| 480 |
"specific_heat": material.specific_heat,
|
| 481 |
"default_thickness": material.default_thickness,
|
| 482 |
"embodied_carbon": material.embodied_carbon,
|
| 483 |
-
"
|
| 484 |
"price": material.price,
|
| 485 |
"emissivity": material.emissivity,
|
| 486 |
"is_edit": False
|
|
@@ -493,7 +507,7 @@ class HVACCalculator:
|
|
| 493 |
"specific_heat": material.specific_heat,
|
| 494 |
"default_thickness": material.default_thickness,
|
| 495 |
"embodied_carbon": material.embodied_carbon,
|
| 496 |
-
"
|
| 497 |
"price": material.price,
|
| 498 |
"emissivity": material.emissivity
|
| 499 |
}
|
|
@@ -552,7 +566,7 @@ class HVACCalculator:
|
|
| 552 |
"specific_heat": material.specific_heat,
|
| 553 |
"default_thickness": material.default_thickness,
|
| 554 |
"embodied_carbon": material.embodied_carbon,
|
| 555 |
-
"
|
| 556 |
"price": material.price,
|
| 557 |
"emissivity": material.emissivity,
|
| 558 |
"is_edit": True,
|
|
@@ -566,7 +580,7 @@ class HVACCalculator:
|
|
| 566 |
"specific_heat": material.specific_heat,
|
| 567 |
"default_thickness": material.default_thickness,
|
| 568 |
"embodied_carbon": material.embodied_carbon,
|
| 569 |
-
"
|
| 570 |
"price": material.price,
|
| 571 |
"emissivity": material.emissivity
|
| 572 |
}
|
|
@@ -596,7 +610,7 @@ class HVACCalculator:
|
|
| 596 |
"specific_heat": 1000.0,
|
| 597 |
"default_thickness": 0.1,
|
| 598 |
"embodied_carbon": 0.5,
|
| 599 |
-
"
|
| 600 |
"price": 50.0,
|
| 601 |
"emissivity": 0.925
|
| 602 |
})
|
|
@@ -624,7 +638,7 @@ class HVACCalculator:
|
|
| 624 |
"Thermal Conductivity (W/m路K)",
|
| 625 |
min_value=0.01,
|
| 626 |
value=form_state.get("conductivity", editor_state.get("conductivity", 0.1)),
|
| 627 |
-
help="Heat flow ease
|
| 628 |
key="material_conductivity_input"
|
| 629 |
)
|
| 630 |
density = st.number_input(
|
|
@@ -655,13 +669,13 @@ class HVACCalculator:
|
|
| 655 |
help="Production carbon emissions",
|
| 656 |
key="material_embodied_carbon_input"
|
| 657 |
)
|
| 658 |
-
|
| 659 |
-
"Solar
|
| 660 |
min_value=0.0,
|
| 661 |
max_value=1.0,
|
| 662 |
-
value=form_state.get("
|
| 663 |
help="Solar radiation absorbed",
|
| 664 |
-
key="
|
| 665 |
)
|
| 666 |
price = st.number_input(
|
| 667 |
"Price (USD/m虏)",
|
|
@@ -675,7 +689,7 @@ class HVACCalculator:
|
|
| 675 |
min_value=0.0,
|
| 676 |
max_value=1.0,
|
| 677 |
value=form_state.get("emissivity", editor_state.get("emissivity", 0.925)),
|
| 678 |
-
help="Ratio of radiation emitted by the material",
|
| 679 |
key="material_emissivity_input"
|
| 680 |
)
|
| 681 |
|
|
@@ -691,7 +705,7 @@ class HVACCalculator:
|
|
| 691 |
"specific_heat": specific_heat,
|
| 692 |
"default_thickness": default_thickness,
|
| 693 |
"embodied_carbon": embodied_carbon,
|
| 694 |
-
"
|
| 695 |
"price": price,
|
| 696 |
"emissivity": emissivity
|
| 697 |
}
|
|
@@ -710,7 +724,7 @@ class HVACCalculator:
|
|
| 710 |
specific_heat=specific_heat,
|
| 711 |
default_thickness=default_thickness,
|
| 712 |
embodied_carbon=embodied_carbon,
|
| 713 |
-
solar_absorption=
|
| 714 |
price=price,
|
| 715 |
emissivity=emissivity,
|
| 716 |
is_library=False
|
|
@@ -732,7 +746,7 @@ class HVACCalculator:
|
|
| 732 |
"specific_heat": 1000.0,
|
| 733 |
"default_thickness": 0.1,
|
| 734 |
"embodied_carbon": 0.5,
|
| 735 |
-
"
|
| 736 |
"price": 50.0,
|
| 737 |
"emissivity": 0.925
|
| 738 |
}
|
|
@@ -750,13 +764,8 @@ class HVACCalculator:
|
|
| 750 |
project_materials=st.session_state.project_materials,
|
| 751 |
only_project=True)
|
| 752 |
if not material_df.empty:
|
| 753 |
-
#
|
| 754 |
-
material_df =
|
| 755 |
-
"Conductivity (W/m路K)": "U-Value (W/m虏路K)",
|
| 756 |
-
"Solar Absorption": "Solar Absorptance"
|
| 757 |
-
})
|
| 758 |
-
# Recompute U-Value using get_u_value()
|
| 759 |
-
material_df["U-Value (W/m虏路K)"] = [m.get_u_value() for m in st.session_state.project_materials.values()]
|
| 760 |
st.dataframe(material_df, use_container_width=True)
|
| 761 |
else:
|
| 762 |
st.write("No project materials to display.")
|
|
@@ -1070,19 +1079,6 @@ class HVACCalculator:
|
|
| 1070 |
project_constructions = construction_df
|
| 1071 |
if construction_filter != "All":
|
| 1072 |
project_constructions = project_constructions[project_constructions['Component Type'] == construction_filter]
|
| 1073 |
-
# Remove Solar Absorption, keep Solar Absorptance
|
| 1074 |
-
if 'Solar Absorption' in project_constructions.columns:
|
| 1075 |
-
project_constructions = project_constructions.drop(columns=['Solar Absorption'])
|
| 1076 |
-
project_constructions['Solar Absorptance'] = [
|
| 1077 |
-
c.layers[0]["material"].solar_absorption if c.layers else 0.6
|
| 1078 |
-
for c in st.session_state.project_constructions.values()
|
| 1079 |
-
if c.component_type == construction_filter or construction_filter == "All"
|
| 1080 |
-
]
|
| 1081 |
-
project_constructions['Emissivity'] = [
|
| 1082 |
-
c.layers[0]["material"].emissivity if c.layers else 0.925
|
| 1083 |
-
for c in st.session_state.project_constructions.values()
|
| 1084 |
-
if c.component_type == construction_filter or construction_filter == "All"
|
| 1085 |
-
]
|
| 1086 |
st.dataframe(project_constructions, use_container_width=True)
|
| 1087 |
else:
|
| 1088 |
st.write("No project constructions to display.")
|
|
@@ -1121,20 +1117,14 @@ class HVACCalculator:
|
|
| 1121 |
"name": glazing.name,
|
| 1122 |
"shgc": glazing.shgc,
|
| 1123 |
"u_value": glazing.u_value,
|
| 1124 |
-
"embodied_carbon": glazing.embodied_carbon,
|
| 1125 |
-
"price": glazing.price,
|
| 1126 |
"h_o": glazing.h_o,
|
| 1127 |
-
"iac": 1.0, # Default IAC
|
| 1128 |
"is_edit": False
|
| 1129 |
}
|
| 1130 |
st.session_state.glazing_form_state = {
|
| 1131 |
"name": glazing.name,
|
| 1132 |
"shgc": glazing.shgc,
|
| 1133 |
"u_value": glazing.u_value,
|
| 1134 |
-
"
|
| 1135 |
-
"price": glazing.price,
|
| 1136 |
-
"h_o": glazing.h_o,
|
| 1137 |
-
"iac": 1.0
|
| 1138 |
}
|
| 1139 |
st.session_state.active_tab = "Glazing"
|
| 1140 |
if cols[4].button("Copy", key=f"copy_lib_glz_{glazing.name}"):
|
|
@@ -1147,17 +1137,12 @@ class HVACCalculator:
|
|
| 1147 |
name=new_name,
|
| 1148 |
shgc=glazing.shgc,
|
| 1149 |
u_value=glazing.u_value,
|
| 1150 |
-
embodied_carbon=glazing.embodied_carbon,
|
| 1151 |
-
price=glazing.price,
|
| 1152 |
h_o=glazing.h_o,
|
| 1153 |
is_library=False
|
| 1154 |
)
|
| 1155 |
-
|
| 1156 |
-
|
| 1157 |
-
|
| 1158 |
-
st.session_state.rerun_pending = True
|
| 1159 |
-
else:
|
| 1160 |
-
st.error(message)
|
| 1161 |
|
| 1162 |
st.subheader("Project Glazing")
|
| 1163 |
with st.container():
|
|
@@ -1180,10 +1165,7 @@ class HVACCalculator:
|
|
| 1180 |
"name": glazing.name,
|
| 1181 |
"shgc": glazing.shgc,
|
| 1182 |
"u_value": glazing.u_value,
|
| 1183 |
-
"embodied_carbon": glazing.embodied_carbon,
|
| 1184 |
-
"price": glazing.price,
|
| 1185 |
"h_o": glazing.h_o,
|
| 1186 |
-
"iac": 1.0, # Default IAC
|
| 1187 |
"is_edit": True,
|
| 1188 |
"original_name": glazing.name
|
| 1189 |
}
|
|
@@ -1191,21 +1173,17 @@ class HVACCalculator:
|
|
| 1191 |
"name": glazing.name,
|
| 1192 |
"shgc": glazing.shgc,
|
| 1193 |
"u_value": glazing.u_value,
|
| 1194 |
-
"
|
| 1195 |
-
"price": glazing.price,
|
| 1196 |
-
"h_o": glazing.h_o,
|
| 1197 |
-
"iac": 1.0
|
| 1198 |
}
|
| 1199 |
st.session_state.active_tab = "Glazing"
|
| 1200 |
if cols[4].button("Delete", key=f"delete_proj_glz_{glazing.name}"):
|
| 1201 |
-
|
| 1202 |
-
|
| 1203 |
-
|
| 1204 |
-
if success:
|
| 1205 |
-
st.success(message)
|
| 1206 |
-
st.session_state.rerun_pending = True
|
| 1207 |
else:
|
| 1208 |
-
st.
|
|
|
|
|
|
|
| 1209 |
else:
|
| 1210 |
st.write("No project glazing materials added.")
|
| 1211 |
|
|
@@ -1217,11 +1195,8 @@ class HVACCalculator:
|
|
| 1217 |
form_state = st.session_state.get("glazing_form_state", {
|
| 1218 |
"name": "",
|
| 1219 |
"shgc": 0.7,
|
| 1220 |
-
"u_value":
|
| 1221 |
-
"
|
| 1222 |
-
"price": 100.0,
|
| 1223 |
-
"h_o": 17.8,
|
| 1224 |
-
"iac": 1.0
|
| 1225 |
})
|
| 1226 |
is_edit = editor_state.get("is_edit", False)
|
| 1227 |
original_name = editor_state.get("original_name", "")
|
|
@@ -1236,45 +1211,23 @@ class HVACCalculator:
|
|
| 1236 |
min_value=0.0,
|
| 1237 |
max_value=1.0,
|
| 1238 |
value=form_state.get("shgc", editor_state.get("shgc", 0.7)),
|
| 1239 |
-
help="
|
| 1240 |
key="glazing_shgc_input"
|
| 1241 |
)
|
| 1242 |
u_value = st.number_input(
|
| 1243 |
"U-Value (W/m虏路K)",
|
| 1244 |
min_value=0.1,
|
| 1245 |
-
value=form_state.get("u_value", editor_state.get("u_value",
|
| 1246 |
help="Thermal transmittance",
|
| 1247 |
key="glazing_u_value_input"
|
| 1248 |
)
|
| 1249 |
-
embodied_carbon = st.number_input(
|
| 1250 |
-
"Embodied Carbon (kgCO鈧俥/m虏)",
|
| 1251 |
-
min_value=0.0,
|
| 1252 |
-
value=form_state.get("embodied_carbon", editor_state.get("embodied_carbon", 40.0)),
|
| 1253 |
-
help="Production carbon emissions",
|
| 1254 |
-
key="glazing_embodied_carbon_input"
|
| 1255 |
-
)
|
| 1256 |
-
price = st.number_input(
|
| 1257 |
-
"Price (USD/m虏)",
|
| 1258 |
-
min_value=0.0,
|
| 1259 |
-
value=form_state.get("price", editor_state.get("price", 100.0)),
|
| 1260 |
-
help="Cost per area",
|
| 1261 |
-
key="glazing_price_input"
|
| 1262 |
-
)
|
| 1263 |
h_o = st.number_input(
|
| 1264 |
-
"Exterior Surface Conductance (
|
| 1265 |
min_value=0.0,
|
| 1266 |
-
value=form_state.get("h_o", editor_state.get("h_o",
|
| 1267 |
help="Exterior surface heat transfer coefficient",
|
| 1268 |
key="glazing_h_o_input"
|
| 1269 |
)
|
| 1270 |
-
iac = st.number_input(
|
| 1271 |
-
"Interior Attenuation Coefficient (IAC)",
|
| 1272 |
-
min_value=0.0,
|
| 1273 |
-
max_value=1.0,
|
| 1274 |
-
value=form_state.get("iac", editor_state.get("iac", 1.0)),
|
| 1275 |
-
help="Interior solar attenuation",
|
| 1276 |
-
key="glazing_iac_input"
|
| 1277 |
-
)
|
| 1278 |
|
| 1279 |
if st.form_submit_button("Save Glazing"):
|
| 1280 |
action_id = str(uuid.uuid4())
|
|
@@ -1284,68 +1237,82 @@ class HVACCalculator:
|
|
| 1284 |
"name": name,
|
| 1285 |
"shgc": shgc,
|
| 1286 |
"u_value": u_value,
|
| 1287 |
-
"
|
| 1288 |
-
"price": price,
|
| 1289 |
-
"h_o": h_o,
|
| 1290 |
-
"iac": iac
|
| 1291 |
}
|
| 1292 |
if not name or not name.strip():
|
| 1293 |
st.error("Glazing name cannot be empty.")
|
| 1294 |
elif (name in st.session_state.project_glazing_materials or name in getattr(self.material_library, 'library_glazing_materials', {})) and (not is_edit or name != original_name):
|
| 1295 |
-
st.error(f"Glazing '{name}' already exists.")
|
| 1296 |
else:
|
| 1297 |
try:
|
|
|
|
| 1298 |
new_glazing = GlazingMaterial(
|
| 1299 |
name=name,
|
| 1300 |
shgc=shgc,
|
| 1301 |
u_value=u_value,
|
| 1302 |
-
embodied_carbon=embodied_carbon,
|
| 1303 |
-
price=price,
|
| 1304 |
h_o=h_o,
|
| 1305 |
is_library=False
|
| 1306 |
)
|
| 1307 |
-
|
| 1308 |
-
|
| 1309 |
-
success
|
| 1310 |
-
original_name, new_glazing, st.session_state.project_glazing_materials, st.session_state.components
|
| 1311 |
-
)
|
| 1312 |
-
else:
|
| 1313 |
-
success, message = self.material_library.add_project_glazing_material(new_glazing, st.session_state.project_glazing_materials)
|
| 1314 |
-
if success:
|
| 1315 |
-
st.success(message)
|
| 1316 |
-
st.session_state.glazing_editor = {}
|
| 1317 |
-
st.session_state.glazing_form_state = {
|
| 1318 |
-
"name": "",
|
| 1319 |
-
"shgc": 0.7,
|
| 1320 |
-
"u_value": 2.0,
|
| 1321 |
-
"embodied_carbon": 40.0,
|
| 1322 |
-
"price": 100.0,
|
| 1323 |
-
"h_o": 17.8,
|
| 1324 |
-
"iac": 1.0
|
| 1325 |
-
}
|
| 1326 |
-
st.session_state.glazing_action = {"action": None, "id": None}
|
| 1327 |
-
st.session_state.rerun_trigger = None
|
| 1328 |
-
st.session_state.rerun_pending = True
|
| 1329 |
else:
|
| 1330 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1331 |
except Exception as e:
|
| 1332 |
-
st.error(f"Error saving glazing: {str(e)}")
|
| 1333 |
|
| 1334 |
st.subheader("Project Glazing")
|
| 1335 |
-
|
| 1336 |
-
|
| 1337 |
-
|
| 1338 |
-
|
| 1339 |
-
|
| 1340 |
-
|
| 1341 |
-
|
| 1342 |
-
|
| 1343 |
-
|
| 1344 |
-
|
| 1345 |
-
|
| 1346 |
-
|
| 1347 |
-
|
| 1348 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1349 |
|
| 1350 |
with tab4:
|
| 1351 |
st.subheader("Doors")
|
|
@@ -1362,44 +1329,28 @@ class HVACCalculator:
|
|
| 1362 |
library_door_list = []
|
| 1363 |
cols = st.columns([2, 1, 1, 1, 1])
|
| 1364 |
cols[0].write("**Name**")
|
| 1365 |
-
cols[1].write("**
|
| 1366 |
-
cols[2].write("**
|
| 1367 |
cols[3].write("**Preview**")
|
| 1368 |
cols[4].write("**Copy**")
|
| 1369 |
for door in library_door_list:
|
| 1370 |
cols = st.columns([2, 1, 1, 1, 1])
|
| 1371 |
cols[0].write(door.name)
|
| 1372 |
-
cols[1].write(f"{door.
|
| 1373 |
-
cols[2].write(f"{door.
|
| 1374 |
if cols[3].button("Preview", key=f"preview_lib_door_{door.name}"):
|
| 1375 |
if st.session_state.get("rerun_trigger") != f"preview_door_{door.name}":
|
| 1376 |
st.session_state.rerun_trigger = f"preview_door_{door.name}"
|
| 1377 |
st.session_state.door_editor = {
|
| 1378 |
"name": door.name,
|
| 1379 |
-
"category": door.category.value,
|
| 1380 |
-
"conductivity": door.conductivity,
|
| 1381 |
-
"density": door.density,
|
| 1382 |
-
"specific_heat": door.specific_heat,
|
| 1383 |
-
"default_thickness": door.default_thickness,
|
| 1384 |
"u_value": door.u_value,
|
| 1385 |
-
"solar_absorption": door.solar_absorption,
|
| 1386 |
-
"embodied_carbon": door.embodied_carbon,
|
| 1387 |
-
"price": door.price,
|
| 1388 |
-
"emissivity": door.emissivity,
|
| 1389 |
"is_edit": False
|
| 1390 |
}
|
| 1391 |
st.session_state.door_form_state = {
|
| 1392 |
"name": door.name,
|
| 1393 |
-
"category": door.category.value,
|
| 1394 |
-
"conductivity": door.conductivity,
|
| 1395 |
-
"density": door.density,
|
| 1396 |
-
"specific_heat": door.specific_heat,
|
| 1397 |
-
"default_thickness": door.default_thickness,
|
| 1398 |
"u_value": door.u_value,
|
| 1399 |
-
"solar_absorption": door.solar_absorption
|
| 1400 |
-
"embodied_carbon": door.embodied_carbon,
|
| 1401 |
-
"price": door.price,
|
| 1402 |
-
"emissivity": door.emissivity
|
| 1403 |
}
|
| 1404 |
st.session_state.active_tab = "Doors"
|
| 1405 |
if cols[4].button("Copy", key=f"copy_lib_door_{door.name}"):
|
|
@@ -1410,211 +1361,168 @@ class HVACCalculator:
|
|
| 1410 |
counter += 1
|
| 1411 |
new_door = DoorMaterial(
|
| 1412 |
name=new_name,
|
| 1413 |
-
category=door.category,
|
| 1414 |
-
conductivity=door.conductivity,
|
| 1415 |
-
density=door.density,
|
| 1416 |
-
specific_heat=door.specific_heat,
|
| 1417 |
-
default_thickness=door.default_thickness,
|
| 1418 |
u_value=door.u_value,
|
| 1419 |
solar_absorption=door.solar_absorption,
|
| 1420 |
-
embodied_carbon=door.embodied_carbon,
|
| 1421 |
-
price=door.price,
|
| 1422 |
-
emissivity=door.emissivity,
|
| 1423 |
is_library=False
|
| 1424 |
)
|
| 1425 |
-
|
| 1426 |
-
|
| 1427 |
-
|
| 1428 |
-
st.session_state.rerun_pending = True
|
| 1429 |
-
else:
|
| 1430 |
-
st.error(message)
|
| 1431 |
-
|
| 1432 |
-
with col2:
|
| 1433 |
-
st.subheader("Door Editor/Creator")
|
| 1434 |
-
with st.container():
|
| 1435 |
-
with st.form("door_editor_form", clear_on_submit=False):
|
| 1436 |
-
editor_state = st.session_state.get("door_editor", {})
|
| 1437 |
-
form_state = st.session_state.get("door_form_state", {
|
| 1438 |
-
"name": "",
|
| 1439 |
-
"category": "Sub-Structural Materials",
|
| 1440 |
-
"conductivity": 0.20,
|
| 1441 |
-
"density": 20.0,
|
| 1442 |
-
"specific_heat": 2000.0,
|
| 1443 |
-
"default_thickness": 0.05,
|
| 1444 |
-
"u_value": 2.0,
|
| 1445 |
-
"solar_absorption": 0.6, # Changed to match material_library.py
|
| 1446 |
-
"embodied_carbon": 20.0,
|
| 1447 |
-
"price": 200.0,
|
| 1448 |
-
"emissivity": 0.925
|
| 1449 |
-
})
|
| 1450 |
-
is_edit = editor_state.get("is_edit", False)
|
| 1451 |
-
original_name = editor_state.get("original_name", "")
|
| 1452 |
-
name = st.text_input(
|
| 1453 |
-
"Door Material Name",
|
| 1454 |
-
value=form_state.get("name", editor_state.get("name", "")),
|
| 1455 |
-
help="Unique door material identifier",
|
| 1456 |
-
key="door_name_input"
|
| 1457 |
-
)
|
| 1458 |
-
default_category = form_state.get("category", editor_state.get("category", "Sub-Structural Materials"))
|
| 1459 |
-
category_index = ([c.value for c in MaterialCategory].index(default_category)
|
| 1460 |
-
if default_category in [c.value for c in MaterialCategory] else 0)
|
| 1461 |
-
category = st.selectbox(
|
| 1462 |
-
"Category",
|
| 1463 |
-
[c.value for c in MaterialCategory],
|
| 1464 |
-
index=category_index,
|
| 1465 |
-
help="Material type classification",
|
| 1466 |
-
key="door_category_input"
|
| 1467 |
-
)
|
| 1468 |
-
conductivity = st.number_input(
|
| 1469 |
-
"Thermal Conductivity (W/m路K)",
|
| 1470 |
-
min_value=0.01,
|
| 1471 |
-
value=form_state.get("conductivity", editor_state.get("conductivity", 0.25)),
|
| 1472 |
-
help="Heat flow ease",
|
| 1473 |
-
key="door_conductivity_input"
|
| 1474 |
-
)
|
| 1475 |
-
density = st.number_input(
|
| 1476 |
-
"Density (kg/m鲁)",
|
| 1477 |
-
min_value=1.0,
|
| 1478 |
-
value=form_state.get("density", editor_state.get("density", 20.0)),
|
| 1479 |
-
help="Mass per volume",
|
| 1480 |
-
key="door_density_input"
|
| 1481 |
-
)
|
| 1482 |
-
specific_heat = st.number_input(
|
| 1483 |
-
"Specific Heat (J/kg路K)",
|
| 1484 |
-
min_value=100.0, # Aligned with DoorMaterial
|
| 1485 |
-
value=form_state.get("specific_heat", editor_state.get("specific_heat", 2000.0)),
|
| 1486 |
-
help="Heat storage capacity",
|
| 1487 |
-
key="door_specific_heat_input"
|
| 1488 |
-
)
|
| 1489 |
-
default_thickness = st.number_input(
|
| 1490 |
-
"Default Thickness (m)",
|
| 1491 |
-
min_value=0.01, # Aligned with DoorMaterial
|
| 1492 |
-
value=form_state.get("default_thickness", editor_state.get("default_thickness", 0.05)),
|
| 1493 |
-
help="Typical thickness",
|
| 1494 |
-
key="door_thickness_input"
|
| 1495 |
-
)
|
| 1496 |
-
u_value = st.number_input(
|
| 1497 |
-
"U-Value (W/m虏路K)",
|
| 1498 |
-
min_value=0.1,
|
| 1499 |
-
value=form_state.get("u_value", editor_state.get("u_value", 2.0)),
|
| 1500 |
-
help="Thermal conductance of the door",
|
| 1501 |
-
key="door_u_value_input"
|
| 1502 |
-
)
|
| 1503 |
-
solar_absorption = st.number_input(
|
| 1504 |
-
"Solar Absorption", # Changed to match material_library.py
|
| 1505 |
-
min_value=0.0,
|
| 1506 |
-
max_value=1.0,
|
| 1507 |
-
value=form_state.get("solar_absorption", editor_state.get("solar_absorption", 0.6)),
|
| 1508 |
-
help="Solar radiation absorbed by the door",
|
| 1509 |
-
key="door_absorption_input"
|
| 1510 |
-
)
|
| 1511 |
-
embodied_carbon = st.number_input(
|
| 1512 |
-
"Embodied Carbon (kgCO鈧俥/m虏)",
|
| 1513 |
-
min_value=0.0,
|
| 1514 |
-
value=form_state.get("embodied_carbon", editor_state.get("embodied_carbon", 20.0)),
|
| 1515 |
-
help="Production carbon emissions",
|
| 1516 |
-
key="door_embodied_carbon_input"
|
| 1517 |
-
)
|
| 1518 |
-
price = st.number_input(
|
| 1519 |
-
"Price (USD/m虏)",
|
| 1520 |
-
min_value=0.0,
|
| 1521 |
-
value=form_state.get("price", editor_state.get("price", 200.0)),
|
| 1522 |
-
help="Cost per area",
|
| 1523 |
-
key="door_price_input"
|
| 1524 |
-
)
|
| 1525 |
-
emissivity = st.number_input(
|
| 1526 |
-
"Emissivity",
|
| 1527 |
-
min_value=0.0,
|
| 1528 |
-
max_value=1.0,
|
| 1529 |
-
value=form_state.get("emissivity", editor_state.get("emissivity", 0.925)),
|
| 1530 |
-
help="Ratio of radiation emitted by the door",
|
| 1531 |
-
key="door_emissivity_input"
|
| 1532 |
-
)
|
| 1533 |
|
| 1534 |
-
|
| 1535 |
-
|
| 1536 |
-
|
| 1537 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1538 |
st.session_state.door_form_state = {
|
| 1539 |
-
"name": name,
|
| 1540 |
-
"
|
| 1541 |
-
"
|
| 1542 |
-
"density": density,
|
| 1543 |
-
"specific_heat": specific_heat,
|
| 1544 |
-
"default_thickness": default_thickness,
|
| 1545 |
-
"u_value": u_value,
|
| 1546 |
-
"solar_absorption": solar_absorption, # Changed to match material_library.py
|
| 1547 |
-
"embodied_carbon": embodied_carbon,
|
| 1548 |
-
"price": price,
|
| 1549 |
-
"emissivity": emissivity
|
| 1550 |
}
|
| 1551 |
-
|
| 1552 |
-
|
| 1553 |
-
|
| 1554 |
-
|
| 1555 |
-
|
| 1556 |
-
|
| 1557 |
-
|
| 1558 |
-
|
| 1559 |
-
|
| 1560 |
-
|
| 1561 |
-
|
| 1562 |
-
density=density,
|
| 1563 |
-
specific_heat=specific_heat,
|
| 1564 |
-
default_thickness=default_thickness,
|
| 1565 |
-
u_value=u_value,
|
| 1566 |
-
solar_absorption=solar_absorption,
|
| 1567 |
-
embodied_carbon=embodied_carbon,
|
| 1568 |
-
price=price,
|
| 1569 |
-
emissivity=emissivity,
|
| 1570 |
-
is_library=False
|
| 1571 |
-
)
|
| 1572 |
-
if is_edit:
|
| 1573 |
-
success, message = self.material_library.edit_project_door_material(
|
| 1574 |
-
original_name, new_door, st.session_state.project_door_materials, st.session_state.components
|
| 1575 |
-
)
|
| 1576 |
-
else:
|
| 1577 |
-
success, message = self.material_library.add_project_door_material(new_door, st.session_state.project_door_materials)
|
| 1578 |
-
if success:
|
| 1579 |
-
st.success(message)
|
| 1580 |
-
st.session_state.door_editor = {}
|
| 1581 |
-
st.session_state.door_form_state = {
|
| 1582 |
-
"name": "",
|
| 1583 |
-
"category": "Sub-Structural Materials",
|
| 1584 |
-
"conductivity": 0.20,
|
| 1585 |
-
"density": 20.0,
|
| 1586 |
-
"specific_heat": 2000.0,
|
| 1587 |
-
"default_thickness": 0.05,
|
| 1588 |
-
"u_value": 2.0,
|
| 1589 |
-
"solar_absorption": 0.6, # Changed to match material_library.py
|
| 1590 |
-
"embodied_carbon": 20.0,
|
| 1591 |
-
"price": 200.0,
|
| 1592 |
-
"emissivity": 0.925
|
| 1593 |
-
}
|
| 1594 |
-
st.session_state.door_action = {"action": None, "id": None}
|
| 1595 |
-
st.session_state.rerun_trigger = None
|
| 1596 |
-
st.session_state.rerun_pending = True
|
| 1597 |
-
else:
|
| 1598 |
-
st.error(f"Failed to save door material: {message}")
|
| 1599 |
-
except Exception as e:
|
| 1600 |
-
st.error(f"Error saving door material: {str(e)}")
|
| 1601 |
|
| 1602 |
-
|
| 1603 |
-
|
| 1604 |
-
|
| 1605 |
-
|
| 1606 |
-
|
| 1607 |
-
|
| 1608 |
-
|
| 1609 |
-
|
| 1610 |
-
|
| 1611 |
-
|
| 1612 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1613 |
else:
|
| 1614 |
st.write("No project door materials to display.")
|
| 1615 |
-
except Exception as e:
|
| 1616 |
-
st.error(f"Error displaying project doors: {str(e)}")
|
| 1617 |
-
st.write("No door materials to display.")
|
| 1618 |
|
| 1619 |
col1, col2 = st.columns(2)
|
| 1620 |
with col1:
|
|
|
|
| 18 |
from typing import Dict, List, Any, Optional, Tuple
|
| 19 |
from enum import Enum
|
| 20 |
import scipy.linalg as linalg
|
| 21 |
+
from data.material_library import MaterialLibrary, MaterialCategory, Construction, Material, GlazingMaterial, DoorMaterial
|
| 22 |
from data.climate_data import ClimateData, ClimateLocation
|
| 23 |
import streamlit.components.v1 as components
|
| 24 |
from data.calculation import TFMCalculations, ComponentType
|
|
|
|
| 44 |
self.h_o = h_o
|
| 45 |
self.is_library = is_library
|
| 46 |
|
| 47 |
+
class DoorMaterial:
|
| 48 |
+
def __init__(self, name: str, u_value: float, solar_absorption: float, is_library: bool = True):
|
| 49 |
+
self.name = name
|
| 50 |
+
self.u_value = u_value
|
| 51 |
+
self.solar_absorption = solar_absorption
|
| 52 |
+
self.is_library = is_library
|
| 53 |
+
|
| 54 |
class GlazingMaterial:
|
| 55 |
def __init__(self, name: str, shgc: float, u_value: float, h_o: float, is_library: bool = True):
|
| 56 |
self.name = name
|
|
|
|
| 59 |
self.h_o = h_o
|
| 60 |
self.is_library = is_library
|
| 61 |
|
| 62 |
+
class DoorMaterial:
|
| 63 |
+
def __init__(self, name: str, u_value: float, solar_absorption: float, is_library: bool = True):
|
| 64 |
+
self.name = name
|
| 65 |
+
self.u_value = u_value
|
| 66 |
+
self.solar_absorption = solar_absorption
|
| 67 |
+
self.is_library = is_library
|
| 68 |
+
|
| 69 |
class Component:
|
| 70 |
def __init__(self, name: str, component_type: ComponentType, area: float, elevation: Optional[str] = None,
|
| 71 |
orientation: Optional[float] = None, rotation: float = 0.0, tilt: float = None,
|
| 72 |
construction: Optional[Construction] = None, glazing_material: Optional[GlazingMaterial] = None,
|
| 73 |
+
door_material: Optional[DoorMaterial] = None, layers: List[Dict] = None, u_value: float = None,
|
| 74 |
shgc: float = None):
|
| 75 |
self.name = name
|
| 76 |
self.component_type = component_type
|
|
|
|
| 91 |
self.u_value = door_material.u_value
|
| 92 |
else:
|
| 93 |
self.u_value = construction.u_value if construction else self.calculate_u_value()
|
| 94 |
+
self.solar_absorptivity = (door_material.solar_absorption if door_material else
|
| 95 |
+
construction.solar_absorption if construction else 0.6)
|
| 96 |
self.shgc = shgc if shgc is not None else (glazing_material.shgc if glazing_material else None)
|
| 97 |
|
| 98 |
# Compute orientation_angle
|
|
|
|
| 494 |
"specific_heat": material.specific_heat,
|
| 495 |
"default_thickness": material.default_thickness,
|
| 496 |
"embodied_carbon": material.embodied_carbon,
|
| 497 |
+
"solar_absorption": material.solar_absorption,
|
| 498 |
"price": material.price,
|
| 499 |
"emissivity": material.emissivity,
|
| 500 |
"is_edit": False
|
|
|
|
| 507 |
"specific_heat": material.specific_heat,
|
| 508 |
"default_thickness": material.default_thickness,
|
| 509 |
"embodied_carbon": material.embodied_carbon,
|
| 510 |
+
"solar_absorption": material.solar_absorption,
|
| 511 |
"price": material.price,
|
| 512 |
"emissivity": material.emissivity
|
| 513 |
}
|
|
|
|
| 566 |
"specific_heat": material.specific_heat,
|
| 567 |
"default_thickness": material.default_thickness,
|
| 568 |
"embodied_carbon": material.embodied_carbon,
|
| 569 |
+
"solar_absorption": material.solar_absorption,
|
| 570 |
"price": material.price,
|
| 571 |
"emissivity": material.emissivity,
|
| 572 |
"is_edit": True,
|
|
|
|
| 580 |
"specific_heat": material.specific_heat,
|
| 581 |
"default_thickness": material.default_thickness,
|
| 582 |
"embodied_carbon": material.embodied_carbon,
|
| 583 |
+
"solar_absorption": material.solar_absorption,
|
| 584 |
"price": material.price,
|
| 585 |
"emissivity": material.emissivity
|
| 586 |
}
|
|
|
|
| 610 |
"specific_heat": 1000.0,
|
| 611 |
"default_thickness": 0.1,
|
| 612 |
"embodied_carbon": 0.5,
|
| 613 |
+
"solar_absorption": 0.6,
|
| 614 |
"price": 50.0,
|
| 615 |
"emissivity": 0.925
|
| 616 |
})
|
|
|
|
| 638 |
"Thermal Conductivity (W/m路K)",
|
| 639 |
min_value=0.01,
|
| 640 |
value=form_state.get("conductivity", editor_state.get("conductivity", 0.1)),
|
| 641 |
+
help="Heat flow ease",
|
| 642 |
key="material_conductivity_input"
|
| 643 |
)
|
| 644 |
density = st.number_input(
|
|
|
|
| 669 |
help="Production carbon emissions",
|
| 670 |
key="material_embodied_carbon_input"
|
| 671 |
)
|
| 672 |
+
solar_absorption = st.number_input(
|
| 673 |
+
"Solar Absorption",
|
| 674 |
min_value=0.0,
|
| 675 |
max_value=1.0,
|
| 676 |
+
value=form_state.get("solar_absorption", editor_state.get("solar_absorption", 0.6)),
|
| 677 |
help="Solar radiation absorbed",
|
| 678 |
+
key="material_solar_absorption_input"
|
| 679 |
)
|
| 680 |
price = st.number_input(
|
| 681 |
"Price (USD/m虏)",
|
|
|
|
| 689 |
min_value=0.0,
|
| 690 |
max_value=1.0,
|
| 691 |
value=form_state.get("emissivity", editor_state.get("emissivity", 0.925)),
|
| 692 |
+
help="Ratio of radiation emitted by the material (0.0 to 1.0)",
|
| 693 |
key="material_emissivity_input"
|
| 694 |
)
|
| 695 |
|
|
|
|
| 705 |
"specific_heat": specific_heat,
|
| 706 |
"default_thickness": default_thickness,
|
| 707 |
"embodied_carbon": embodied_carbon,
|
| 708 |
+
"solar_absorption": solar_absorption,
|
| 709 |
"price": price,
|
| 710 |
"emissivity": emissivity
|
| 711 |
}
|
|
|
|
| 724 |
specific_heat=specific_heat,
|
| 725 |
default_thickness=default_thickness,
|
| 726 |
embodied_carbon=embodied_carbon,
|
| 727 |
+
solar_absorption=solar_absorption,
|
| 728 |
price=price,
|
| 729 |
emissivity=emissivity,
|
| 730 |
is_library=False
|
|
|
|
| 746 |
"specific_heat": 1000.0,
|
| 747 |
"default_thickness": 0.1,
|
| 748 |
"embodied_carbon": 0.5,
|
| 749 |
+
"solar_absorption": 0.6,
|
| 750 |
"price": 50.0,
|
| 751 |
"emissivity": 0.925
|
| 752 |
}
|
|
|
|
| 764 |
project_materials=st.session_state.project_materials,
|
| 765 |
only_project=True)
|
| 766 |
if not material_df.empty:
|
| 767 |
+
# Ensure emissivity is included in the DataFrame
|
| 768 |
+
material_df['Emissivity'] = [m.emissivity for m in st.session_state.project_materials.values()]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 769 |
st.dataframe(material_df, use_container_width=True)
|
| 770 |
else:
|
| 771 |
st.write("No project materials to display.")
|
|
|
|
| 1079 |
project_constructions = construction_df
|
| 1080 |
if construction_filter != "All":
|
| 1081 |
project_constructions = project_constructions[project_constructions['Component Type'] == construction_filter]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1082 |
st.dataframe(project_constructions, use_container_width=True)
|
| 1083 |
else:
|
| 1084 |
st.write("No project constructions to display.")
|
|
|
|
| 1117 |
"name": glazing.name,
|
| 1118 |
"shgc": glazing.shgc,
|
| 1119 |
"u_value": glazing.u_value,
|
|
|
|
|
|
|
| 1120 |
"h_o": glazing.h_o,
|
|
|
|
| 1121 |
"is_edit": False
|
| 1122 |
}
|
| 1123 |
st.session_state.glazing_form_state = {
|
| 1124 |
"name": glazing.name,
|
| 1125 |
"shgc": glazing.shgc,
|
| 1126 |
"u_value": glazing.u_value,
|
| 1127 |
+
"h_o": glazing.h_o
|
|
|
|
|
|
|
|
|
|
| 1128 |
}
|
| 1129 |
st.session_state.active_tab = "Glazing"
|
| 1130 |
if cols[4].button("Copy", key=f"copy_lib_glz_{glazing.name}"):
|
|
|
|
| 1137 |
name=new_name,
|
| 1138 |
shgc=glazing.shgc,
|
| 1139 |
u_value=glazing.u_value,
|
|
|
|
|
|
|
| 1140 |
h_o=glazing.h_o,
|
| 1141 |
is_library=False
|
| 1142 |
)
|
| 1143 |
+
st.session_state.project_glazing_materials[new_name] = new_glazing
|
| 1144 |
+
st.success(f"Glazing material '{new_name}' copied!")
|
| 1145 |
+
st.session_state.rerun_pending = True
|
|
|
|
|
|
|
|
|
|
| 1146 |
|
| 1147 |
st.subheader("Project Glazing")
|
| 1148 |
with st.container():
|
|
|
|
| 1165 |
"name": glazing.name,
|
| 1166 |
"shgc": glazing.shgc,
|
| 1167 |
"u_value": glazing.u_value,
|
|
|
|
|
|
|
| 1168 |
"h_o": glazing.h_o,
|
|
|
|
| 1169 |
"is_edit": True,
|
| 1170 |
"original_name": glazing.name
|
| 1171 |
}
|
|
|
|
| 1173 |
"name": glazing.name,
|
| 1174 |
"shgc": glazing.shgc,
|
| 1175 |
"u_value": glazing.u_value,
|
| 1176 |
+
"h_o": glazing.h_o
|
|
|
|
|
|
|
|
|
|
| 1177 |
}
|
| 1178 |
st.session_state.active_tab = "Glazing"
|
| 1179 |
if cols[4].button("Delete", key=f"delete_proj_glz_{glazing.name}"):
|
| 1180 |
+
if any(comp.glazing_material and comp.glazing_material.name == glazing.name
|
| 1181 |
+
for comp_list in st.session_state.components.values() for comp in comp_list):
|
| 1182 |
+
st.error(f"Glazing material '{glazing.name}' is used in components and cannot be deleted.")
|
|
|
|
|
|
|
|
|
|
| 1183 |
else:
|
| 1184 |
+
del st.session_state.project_glazing_materials[glazing.name]
|
| 1185 |
+
st.success(f"Glazing material '{glazing.name}' deleted!")
|
| 1186 |
+
st.session_state.rerun_pending = True
|
| 1187 |
else:
|
| 1188 |
st.write("No project glazing materials added.")
|
| 1189 |
|
|
|
|
| 1195 |
form_state = st.session_state.get("glazing_form_state", {
|
| 1196 |
"name": "",
|
| 1197 |
"shgc": 0.7,
|
| 1198 |
+
"u_value": 5.0,
|
| 1199 |
+
"h_o": 23.0
|
|
|
|
|
|
|
|
|
|
| 1200 |
})
|
| 1201 |
is_edit = editor_state.get("is_edit", False)
|
| 1202 |
original_name = editor_state.get("original_name", "")
|
|
|
|
| 1211 |
min_value=0.0,
|
| 1212 |
max_value=1.0,
|
| 1213 |
value=form_state.get("shgc", editor_state.get("shgc", 0.7)),
|
| 1214 |
+
help="Fraction of solar radiation admitted",
|
| 1215 |
key="glazing_shgc_input"
|
| 1216 |
)
|
| 1217 |
u_value = st.number_input(
|
| 1218 |
"U-Value (W/m虏路K)",
|
| 1219 |
min_value=0.1,
|
| 1220 |
+
value=form_state.get("u_value", editor_state.get("u_value", 5.0)),
|
| 1221 |
help="Thermal transmittance",
|
| 1222 |
key="glazing_u_value_input"
|
| 1223 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1224 |
h_o = st.number_input(
|
| 1225 |
+
"Exterior Surface Conductance (W/m虏路K)",
|
| 1226 |
min_value=0.0,
|
| 1227 |
+
value=form_state.get("h_o", editor_state.get("h_o", 23.0)),
|
| 1228 |
help="Exterior surface heat transfer coefficient",
|
| 1229 |
key="glazing_h_o_input"
|
| 1230 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1231 |
|
| 1232 |
if st.form_submit_button("Save Glazing"):
|
| 1233 |
action_id = str(uuid.uuid4())
|
|
|
|
| 1237 |
"name": name,
|
| 1238 |
"shgc": shgc,
|
| 1239 |
"u_value": u_value,
|
| 1240 |
+
"h_o": h_o
|
|
|
|
|
|
|
|
|
|
| 1241 |
}
|
| 1242 |
if not name or not name.strip():
|
| 1243 |
st.error("Glazing name cannot be empty.")
|
| 1244 |
elif (name in st.session_state.project_glazing_materials or name in getattr(self.material_library, 'library_glazing_materials', {})) and (not is_edit or name != original_name):
|
| 1245 |
+
st.error(f"Glazing material '{name}' already exists.")
|
| 1246 |
else:
|
| 1247 |
try:
|
| 1248 |
+
save_as_new = is_edit and name != original_name
|
| 1249 |
new_glazing = GlazingMaterial(
|
| 1250 |
name=name,
|
| 1251 |
shgc=shgc,
|
| 1252 |
u_value=u_value,
|
|
|
|
|
|
|
| 1253 |
h_o=h_o,
|
| 1254 |
is_library=False
|
| 1255 |
)
|
| 1256 |
+
if is_edit and not save_as_new:
|
| 1257 |
+
st.session_state.project_glazing_materials[original_name] = new_glazing
|
| 1258 |
+
st.success(f"Glazing material '{name}' updated!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1259 |
else:
|
| 1260 |
+
st.session_state.project_glazing_materials[name] = new_glazing
|
| 1261 |
+
st.success(f"Glazing material '{name}' added!")
|
| 1262 |
+
st.session_state.glazing_editor = {}
|
| 1263 |
+
st.session_state.glazing_form_state = {
|
| 1264 |
+
"name": "",
|
| 1265 |
+
"shgc": 0.7,
|
| 1266 |
+
"u_value": 5.0,
|
| 1267 |
+
"h_o": 23.0
|
| 1268 |
+
}
|
| 1269 |
+
st.session_state.glazing_action = {"action": None, "id": None}
|
| 1270 |
+
st.session_state.rerun_trigger = None
|
| 1271 |
+
st.session_state.rerun_pending = True
|
| 1272 |
except Exception as e:
|
| 1273 |
+
st.error(f"Error saving glazing material: {str(e)}")
|
| 1274 |
|
| 1275 |
st.subheader("Project Glazing")
|
| 1276 |
+
if st.session_state.project_glazing_materials:
|
| 1277 |
+
try:
|
| 1278 |
+
glazing_df = self.material_library.to_dataframe("glazing",
|
| 1279 |
+
project_glazing_materials=st.session_state.project_glazing_materials,
|
| 1280 |
+
only_project=True)
|
| 1281 |
+
if glazing_df.empty:
|
| 1282 |
+
# Fallback: Construct DataFrame manually
|
| 1283 |
+
glazing_data = [
|
| 1284 |
+
{
|
| 1285 |
+
"Name": g.name,
|
| 1286 |
+
"SHGC": g.shgc,
|
| 1287 |
+
"U-Value (W/m虏路K)": g.u_value,
|
| 1288 |
+
"Exterior Conductance (W/m虏路K)": g.h_o
|
| 1289 |
+
}
|
| 1290 |
+
for g in st.session_state.project_glazing_materials.values()
|
| 1291 |
+
]
|
| 1292 |
+
glazing_df = pd.DataFrame(glazing_data)
|
| 1293 |
+
if not glazing_df.empty:
|
| 1294 |
+
st.dataframe(glazing_df, use_container_width=True)
|
| 1295 |
+
else:
|
| 1296 |
+
st.write("No project glazing materials to display.")
|
| 1297 |
+
except Exception as e:
|
| 1298 |
+
# Fallback: Construct DataFrame manually
|
| 1299 |
+
glazing_data = [
|
| 1300 |
+
{
|
| 1301 |
+
"Name": g.name,
|
| 1302 |
+
"SHGC": g.shgc,
|
| 1303 |
+
"U-Value (W/m虏路K)": g.u_value,
|
| 1304 |
+
"Exterior Conductance (W/m虏路K)": g.h_o
|
| 1305 |
+
}
|
| 1306 |
+
for g in st.session_state.project_glazing_materials.values()
|
| 1307 |
+
]
|
| 1308 |
+
glazing_df = pd.DataFrame(glazing_data)
|
| 1309 |
+
if not glazing_df.empty:
|
| 1310 |
+
st.dataframe(glazing_df, use_container_width=True)
|
| 1311 |
+
else:
|
| 1312 |
+
st.error(f"Error displaying project glazing materials: {str(e)}")
|
| 1313 |
+
st.write("No project glazing materials to display.")
|
| 1314 |
+
else:
|
| 1315 |
+
st.write("No project glazing materials to display.")
|
| 1316 |
|
| 1317 |
with tab4:
|
| 1318 |
st.subheader("Doors")
|
|
|
|
| 1329 |
library_door_list = []
|
| 1330 |
cols = st.columns([2, 1, 1, 1, 1])
|
| 1331 |
cols[0].write("**Name**")
|
| 1332 |
+
cols[1].write("**U-Value (W/m虏路K)**")
|
| 1333 |
+
cols[2].write("**Solar Abs.**")
|
| 1334 |
cols[3].write("**Preview**")
|
| 1335 |
cols[4].write("**Copy**")
|
| 1336 |
for door in library_door_list:
|
| 1337 |
cols = st.columns([2, 1, 1, 1, 1])
|
| 1338 |
cols[0].write(door.name)
|
| 1339 |
+
cols[1].write(f"{door.u_value:.3f}")
|
| 1340 |
+
cols[2].write(f"{door.solar_absorption:.2f}")
|
| 1341 |
if cols[3].button("Preview", key=f"preview_lib_door_{door.name}"):
|
| 1342 |
if st.session_state.get("rerun_trigger") != f"preview_door_{door.name}":
|
| 1343 |
st.session_state.rerun_trigger = f"preview_door_{door.name}"
|
| 1344 |
st.session_state.door_editor = {
|
| 1345 |
"name": door.name,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1346 |
"u_value": door.u_value,
|
| 1347 |
+
"solar_absorption": door.solar_absorption,
|
|
|
|
|
|
|
|
|
|
| 1348 |
"is_edit": False
|
| 1349 |
}
|
| 1350 |
st.session_state.door_form_state = {
|
| 1351 |
"name": door.name,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1352 |
"u_value": door.u_value,
|
| 1353 |
+
"solar_absorption": door.solar_absorption
|
|
|
|
|
|
|
|
|
|
| 1354 |
}
|
| 1355 |
st.session_state.active_tab = "Doors"
|
| 1356 |
if cols[4].button("Copy", key=f"copy_lib_door_{door.name}"):
|
|
|
|
| 1361 |
counter += 1
|
| 1362 |
new_door = DoorMaterial(
|
| 1363 |
name=new_name,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1364 |
u_value=door.u_value,
|
| 1365 |
solar_absorption=door.solar_absorption,
|
|
|
|
|
|
|
|
|
|
| 1366 |
is_library=False
|
| 1367 |
)
|
| 1368 |
+
st.session_state.project_door_materials[new_name] = new_door
|
| 1369 |
+
st.success(f"Door material '{new_name}' copied!")
|
| 1370 |
+
st.session_state.rerun_pending = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1371 |
|
| 1372 |
+
st.subheader("Project Doors")
|
| 1373 |
+
with st.container():
|
| 1374 |
+
if st.session_state.project_door_materials:
|
| 1375 |
+
cols = st.columns([2, 1, 1, 1, 1])
|
| 1376 |
+
cols[0].write("**Name**")
|
| 1377 |
+
cols[1].write("**U-Value (W/m虏路K)**")
|
| 1378 |
+
cols[2].write("**Solar Abs.**")
|
| 1379 |
+
cols[3].write("**Edit**")
|
| 1380 |
+
cols[4].write("**Delete**")
|
| 1381 |
+
for door in st.session_state.project_door_materials.values():
|
| 1382 |
+
cols = st.columns([2, 1, 1, 1, 1])
|
| 1383 |
+
cols[0].write(door.name)
|
| 1384 |
+
cols[1].write(f"{door.u_value:.3f}")
|
| 1385 |
+
cols[2].write(f"{door.solar_absorption:.2f}")
|
| 1386 |
+
if cols[3].button("Edit", key=f"edit_proj_door_{door.name}"):
|
| 1387 |
+
if st.session_state.get("rerun_trigger") != f"edit_door_{door.name}":
|
| 1388 |
+
st.session_state.rerun_trigger = f"edit_door_{door.name}"
|
| 1389 |
+
st.session_state.door_editor = {
|
| 1390 |
+
"name": door.name,
|
| 1391 |
+
"u_value": door.u_value,
|
| 1392 |
+
"solar_absorption": door.solar_absorption,
|
| 1393 |
+
"is_edit": True,
|
| 1394 |
+
"original_name": door.name
|
| 1395 |
+
}
|
| 1396 |
st.session_state.door_form_state = {
|
| 1397 |
+
"name": door.name,
|
| 1398 |
+
"u_value": door.u_value,
|
| 1399 |
+
"solar_absorption": door.solar_absorption
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1400 |
}
|
| 1401 |
+
st.session_state.active_tab = "Doors"
|
| 1402 |
+
if cols[4].button("Delete", key=f"delete_proj_door_{door.name}"):
|
| 1403 |
+
if any(comp.door_material and comp.door_material.name == door.name
|
| 1404 |
+
for comp_list in st.session_state.components.values() for comp in comp_list):
|
| 1405 |
+
st.error(f"Door material '{door.name}' is used in components and cannot be deleted.")
|
| 1406 |
+
else:
|
| 1407 |
+
del st.session_state.project_door_materials[door.name]
|
| 1408 |
+
st.success(f"Door material '{door.name}' deleted!")
|
| 1409 |
+
st.session_state.rerun_pending = True
|
| 1410 |
+
else:
|
| 1411 |
+
st.write("No project door materials added.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1412 |
|
| 1413 |
+
with col2:
|
| 1414 |
+
st.subheader("Door Editor/Creator")
|
| 1415 |
+
with st.container():
|
| 1416 |
+
with st.form("door_editor_form", clear_on_submit=False):
|
| 1417 |
+
editor_state = st.session_state.get("door_editor", {})
|
| 1418 |
+
form_state = st.session_state.get("door_form_state", {
|
| 1419 |
+
"name": "",
|
| 1420 |
+
"u_value": 2.0,
|
| 1421 |
+
"solar_absorption": 0.6
|
| 1422 |
+
})
|
| 1423 |
+
is_edit = editor_state.get("is_edit", False)
|
| 1424 |
+
original_name = editor_state.get("original_name", "")
|
| 1425 |
+
name = st.text_input(
|
| 1426 |
+
"Door Name",
|
| 1427 |
+
value=form_state.get("name", editor_state.get("name", "")),
|
| 1428 |
+
help="Unique door identifier",
|
| 1429 |
+
key="door_name_input"
|
| 1430 |
+
)
|
| 1431 |
+
u_value = st.number_input(
|
| 1432 |
+
"U-Value (W/m虏路K)",
|
| 1433 |
+
min_value=0.1,
|
| 1434 |
+
value=form_state.get("u_value", editor_state.get("u_value", 2.0)),
|
| 1435 |
+
help="Thermal transmittance",
|
| 1436 |
+
key="door_u_value_input"
|
| 1437 |
+
)
|
| 1438 |
+
solar_absorption = st.number_input(
|
| 1439 |
+
"Solar Absorption",
|
| 1440 |
+
min_value=0.0,
|
| 1441 |
+
max_value=1.0,
|
| 1442 |
+
value=form_state.get("solar_absorption", editor_state.get("solar_absorption", 0.6)),
|
| 1443 |
+
help="Fraction of solar radiation absorbed",
|
| 1444 |
+
key="door_solar_absorption_input"
|
| 1445 |
+
)
|
| 1446 |
+
|
| 1447 |
+
if st.form_submit_button("Save Door"):
|
| 1448 |
+
action_id = str(uuid.uuid4())
|
| 1449 |
+
if st.session_state.door_action.get("id") != action_id:
|
| 1450 |
+
st.session_state.door_action = {"action": "save", "id": action_id}
|
| 1451 |
+
st.session_state.door_form_state = {
|
| 1452 |
+
"name": name,
|
| 1453 |
+
"u_value": u_value,
|
| 1454 |
+
"solar_absorption": solar_absorption
|
| 1455 |
+
}
|
| 1456 |
+
if not name or not name.strip():
|
| 1457 |
+
st.error("Door name cannot be empty.")
|
| 1458 |
+
elif (name in st.session_state.project_door_materials or name in getattr(self.material_library, 'library_door_materials', {})) and (not is_edit or name != original_name):
|
| 1459 |
+
st.error(f"Door material '{name}' already exists.")
|
| 1460 |
+
else:
|
| 1461 |
+
try:
|
| 1462 |
+
save_as_new = is_edit and name != original_name
|
| 1463 |
+
new_door = DoorMaterial(
|
| 1464 |
+
name=name,
|
| 1465 |
+
u_value=u_value,
|
| 1466 |
+
solar_absorption=solar_absorption,
|
| 1467 |
+
is_library=False
|
| 1468 |
+
)
|
| 1469 |
+
if is_edit and not save_as_new:
|
| 1470 |
+
st.session_state.project_door_materials[original_name] = new_door
|
| 1471 |
+
st.success(f"Door material '{name}' updated!")
|
| 1472 |
+
else:
|
| 1473 |
+
st.session_state.project_door_materials[name] = new_door
|
| 1474 |
+
st.success(f"Door material '{name}' added!")
|
| 1475 |
+
st.session_state.door_editor = {}
|
| 1476 |
+
st.session_state.door_form_state = {
|
| 1477 |
+
"name": "",
|
| 1478 |
+
"u_value": 2.0,
|
| 1479 |
+
"solar_absorption": 0.6
|
| 1480 |
+
}
|
| 1481 |
+
st.session_state.door_action = {"action": None, "id": None}
|
| 1482 |
+
st.session_state.rerun_trigger = None
|
| 1483 |
+
st.session_state.rerun_pending = True
|
| 1484 |
+
except Exception as e:
|
| 1485 |
+
st.error(f"Error saving door material: {str(e)}")
|
| 1486 |
+
|
| 1487 |
+
st.subheader("Project Doors")
|
| 1488 |
+
if st.session_state.project_door_materials:
|
| 1489 |
+
try:
|
| 1490 |
+
door_df = self.material_library.to_dataframe("doors",
|
| 1491 |
+
project_door_materials=st.session_state.project_door_materials,
|
| 1492 |
+
only_project=True)
|
| 1493 |
+
if door_df.empty:
|
| 1494 |
+
# Fallback: Construct DataFrame manually
|
| 1495 |
+
door_data = [
|
| 1496 |
+
{
|
| 1497 |
+
"Name": d.name,
|
| 1498 |
+
"U-Value (W/m虏路K)": d.u_value,
|
| 1499 |
+
"Solar Absorption": d.solar_absorption
|
| 1500 |
+
}
|
| 1501 |
+
for d in st.session_state.project_door_materials.values()
|
| 1502 |
+
]
|
| 1503 |
+
door_df = pd.DataFrame(door_data)
|
| 1504 |
+
if not door_df.empty:
|
| 1505 |
+
st.dataframe(door_df, use_container_width=True)
|
| 1506 |
+
else:
|
| 1507 |
+
st.write("No project door materials to display.")
|
| 1508 |
+
except Exception as e:
|
| 1509 |
+
# Fallback: Construct DataFrame manually
|
| 1510 |
+
door_data = [
|
| 1511 |
+
{
|
| 1512 |
+
"Name": d.name,
|
| 1513 |
+
"U-Value (W/m虏路K)": d.u_value,
|
| 1514 |
+
"Solar Absorption": d.solar_absorption
|
| 1515 |
+
}
|
| 1516 |
+
for d in st.session_state.project_door_materials.values()
|
| 1517 |
+
]
|
| 1518 |
+
door_df = pd.DataFrame(door_data)
|
| 1519 |
+
if not door_df.empty:
|
| 1520 |
+
st.dataframe(door_df, use_container_width=True)
|
| 1521 |
+
else:
|
| 1522 |
+
st.error(f"Error displaying project door materials: {str(e)}")
|
| 1523 |
+
st.write("No project door materials to display.")
|
| 1524 |
else:
|
| 1525 |
st.write("No project door materials to display.")
|
|
|
|
|
|
|
|
|
|
| 1526 |
|
| 1527 |
col1, col2 = st.columns(2)
|
| 1528 |
with col1:
|