mabuseif commited on
Commit
fb47cc8
verified
1 Parent(s): 3d36616

Upload main.py

Browse files
Files changed (1) hide show
  1. main.py +268 -360
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[Construction] = None, layers: List[Dict] = None, u_value: float = None,
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.solar_absorptance = (door_material.solar_absorptance if door_material else
81
- construction.solar_absorptance if construction else 0.6)
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
- "solar_absorptance": material.solar_absorption,
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
- "solar_absorptance": material.solar_absorption,
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
- "solar_absorptance": material.solar_absorption,
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
- "solar_absorptance": material.solar_absorption,
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
- "solar_absorptance": 0.6,
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 (used to calculate U-Value)",
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
- solar_absorptance = st.number_input(
659
- "Solar Absorptance",
660
  min_value=0.0,
661
  max_value=1.0,
662
- value=form_state.get("solar_absorptance", editor_state.get("solar_absorptance", 0.6)),
663
  help="Solar radiation absorbed",
664
- key="material_solar_absorptance_input"
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
- "solar_absorptance": solar_absorptance,
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=solar_absorptance,
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
- "solar_absorptance": 0.6,
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
- # Rename columns for consistency
754
- material_df = material_df.rename(columns={
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
- "embodied_carbon": glazing.embodied_carbon,
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
- success, message = self.material_library.add_project_glazing_material(new_glazing, st.session_state.project_glazing_materials)
1156
- if success:
1157
- st.success(message)
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
- "embodied_carbon": glazing.embodied_carbon,
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
- success, message = self.material_library.delete_project_glazing_material(
1202
- glazing.name, st.session_state.project_glazing_materials, st.session_state.components
1203
- )
1204
- if success:
1205
- st.success(message)
1206
- st.session_state.rerun_pending = True
1207
  else:
1208
- st.error(message)
 
 
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": 2.0,
1221
- "embodied_carbon": 40.0,
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="Solar heat gain coefficient",
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", 2.0)),
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 (h_o) (W/m虏路K)",
1265
  min_value=0.0,
1266
- value=form_state.get("h_o", editor_state.get("h_o", 17.8)),
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
- "embodied_carbon": embodied_carbon,
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
- # Note: IAC is not stored in GlazingMaterial as per material_library.py
1308
- if is_edit:
1309
- success, message = self.material_library.edit_project_glazing_material(
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.error(f"Failed to save glazing: {message}")
 
 
 
 
 
 
 
 
 
 
 
1331
  except Exception as e:
1332
- st.error(f"Error saving glazing: {str(e)}")
1333
 
1334
  st.subheader("Project Glazing")
1335
- try:
1336
- glazing_df = self.material_library.to_dataframe("glazing_materials",
1337
- project_glazing_materials=st.session_state.project_glazing_materials,
1338
- only_project=True)
1339
- if not glazing_df.empty:
1340
- # Add IAC and Source
1341
- glazing_df['Interior Attenuation Coefficient (IAC)'] = 1.0 # Default since not in GlazingMaterial
1342
- glazing_df['Source'] = ['Project' if not g.is_library else 'Library' for g in st.session_state.project_glazing_materials.values()]
1343
- st.dataframe(glazing_df, use_container_width=True)
1344
- else:
1345
- st.write("No project glazing materials to display.")
1346
- except Exception as e:
1347
- st.error(f"Error displaying project glazing: {str(e)}")
1348
- st.write("No glazing materials to display.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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("**Solar Absorption**") # Changed to match material_library.py
1366
- cols[2].write("**U-Value (W/m虏路K)**")
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.solar_absorption:.2f}")
1373
- cols[2].write(f"{door.u_value:.3f}")
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, # Changed to match material_library.py
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, # Changed to match material_library.py
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
- success, message = self.material_library.add_project_door_material(new_door, st.session_state.project_door_materials)
1426
- if success:
1427
- st.success(message)
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
- if st.form_submit_button("Save Door Material"):
1535
- action_id = str(uuid.uuid4())
1536
- if st.session_state.door_action.get("id") != action_id:
1537
- st.session_state.door_action = {"action": "save", "id": action_id}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1538
  st.session_state.door_form_state = {
1539
- "name": name,
1540
- "category": category,
1541
- "conductivity": conductivity,
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
- if not name or not name.strip():
1552
- st.error("Door material name cannot be empty.")
1553
- elif (name in st.session_state.project_door_materials or name in getattr(self.material_library, 'library_door_materials', {})) and (
1554
- not is_edit or name != original_name):
1555
- st.error(f"Door material '{name}' already exists.")
1556
- else:
1557
- try:
1558
- new_door = DoorMaterial(
1559
- name=name,
1560
- category=MaterialCategory(category), # Convert string to MaterialCategory enum
1561
- conductivity=conductivity,
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
- st.subheader("Detailed Door Materials")
1603
- try:
1604
- door_df = self.material_library.to_dataframe("door_materials",
1605
- project_door_materials=st.session_state.project_door_materials,
1606
- only_project=True)
1607
- if not door_df.empty:
1608
- # Rename for consistency
1609
- door_df = door_df.rename(columns={
1610
- "Solar Absorption": "Solar Absorptance"
1611
- })
1612
- st.dataframe(door_df, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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: