Spaces:
Sleeping
Sleeping
Update app/materials_library.py
Browse files- app/materials_library.py +210 -242
app/materials_library.py
CHANGED
@@ -332,9 +332,15 @@ def calculate_u_value(thermal_conductivity: float, thickness: float) -> float:
|
|
332 |
r_total = R_SI + r_material + R_SE
|
333 |
return 1 / r_total if r_total > 0 else 0.0
|
334 |
|
335 |
-
def calculate_thermal_mass(density: float, specific_heat: float, thickness: float) ->
|
336 |
-
"""Calculate areal thermal mass for a material."""
|
337 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
|
339 |
def display_materials_page():
|
340 |
"""
|
@@ -396,7 +402,7 @@ def display_materials_tab():
|
|
396 |
if filtered_materials:
|
397 |
cols = st.columns([2, 1, 1, 1, 1])
|
398 |
cols[0].write("**Name**")
|
399 |
-
cols[1].write("**Thermal Mass
|
400 |
cols[2].write("**U-Value (W/m²·K)**")
|
401 |
cols[3].write("**Preview**")
|
402 |
cols[4].write("**Copy**")
|
@@ -407,43 +413,38 @@ def display_materials_tab():
|
|
407 |
thermal_mass = calculate_thermal_mass(material["density"], material["specific_heat"], material["default_thickness"])
|
408 |
u_value = calculate_u_value(material["thermal_conductivity"], material["default_thickness"])
|
409 |
cols[0].write(name)
|
410 |
-
cols[1].write(
|
411 |
cols[2].write(f"{u_value:.3f}")
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
counter += 1
|
443 |
-
st.session_state.project_data["materials"]["project"][new_name] = material.copy()
|
444 |
-
st.success(f"Material '{new_name}' copied to project.")
|
445 |
-
logger.info(f"Copied library material '{name}' as '{new_name}' to project")
|
446 |
-
st.rerun()
|
447 |
else:
|
448 |
st.info("No materials found in the selected category.")
|
449 |
|
@@ -461,7 +462,7 @@ def display_materials_tab():
|
|
461 |
if filtered_project_materials:
|
462 |
cols = st.columns([2, 1, 1, 1, 1])
|
463 |
cols[0].write("**Name**")
|
464 |
-
cols[1].write("**Thermal Mass
|
465 |
cols[2].write("**U-Value (W/m²·K)**")
|
466 |
cols[3].write("**Edit**")
|
467 |
cols[4].write("**Delete**")
|
@@ -472,42 +473,37 @@ def display_materials_tab():
|
|
472 |
thermal_mass = calculate_thermal_mass(material["density"], material["specific_heat"], material["default_thickness"])
|
473 |
u_value = calculate_u_value(material["thermal_conductivity"], material["default_thickness"])
|
474 |
cols[0].write(name)
|
475 |
-
cols[1].write(
|
476 |
cols[2].write(f"{u_value:.3f}")
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
st.session_state.
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
else:
|
507 |
-
del st.session_state.project_data["materials"]["project"][name]
|
508 |
-
st.success(f"Material '{name}' deleted from project.")
|
509 |
-
logger.info(f"Deleted material '{name}' from project")
|
510 |
-
st.rerun()
|
511 |
else:
|
512 |
st.info("No project materials in the selected category.")
|
513 |
|
@@ -531,7 +527,7 @@ def display_materials_tab():
|
|
531 |
"Absorptivity": props["absorptivity"],
|
532 |
"Emissivity": props["emissivity"],
|
533 |
"Colour": props["colour"],
|
534 |
-
"Thermal Mass
|
535 |
"U-Value (W/m²·K)": u_value
|
536 |
})
|
537 |
df = pd.DataFrame(data)
|
@@ -581,38 +577,33 @@ def display_fenestrations_tab():
|
|
581 |
cols[0].write(name)
|
582 |
cols[1].write(fenestration["type"])
|
583 |
cols[2].write(f"{fenestration['u_value']:.2f}")
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
counter += 1
|
612 |
-
st.session_state.project_data["fenestrations"]["project"][new_name] = fenestration.copy()
|
613 |
-
st.success(f"Fenestration '{new_name}' copied to project.")
|
614 |
-
logger.info(f"Copied library fenestration '{name}' as '{new_name}' to project")
|
615 |
-
st.rerun()
|
616 |
else:
|
617 |
st.info("No fenestrations found in the selected type.")
|
618 |
|
@@ -641,37 +632,32 @@ def display_fenestrations_tab():
|
|
641 |
cols[0].write(name)
|
642 |
cols[1].write(fenestration["type"])
|
643 |
cols[2].write(f"{fenestration['u_value']:.2f}")
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
st.session_state.
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
else:
|
671 |
-
del st.session_state.project_data["fenestrations"]["project"][name]
|
672 |
-
st.success(f"Fenestration '{name}' deleted from project.")
|
673 |
-
logger.info(f"Deleted fenestration '{name}' from project")
|
674 |
-
st.rerun()
|
675 |
else:
|
676 |
st.info("No project fenestrations in the selected type.")
|
677 |
|
@@ -732,8 +718,6 @@ def initialize_materials():
|
|
732 |
"original_name": "",
|
733 |
"is_library": False
|
734 |
}
|
735 |
-
if "material_action_id" not in st.session_state:
|
736 |
-
st.session_state.material_action_id = None
|
737 |
|
738 |
def initialize_fenestrations():
|
739 |
"""Initialize fenestrations in session state if not present."""
|
@@ -763,8 +747,6 @@ def initialize_fenestrations():
|
|
763 |
"original_name": "",
|
764 |
"is_library": False
|
765 |
}
|
766 |
-
if "fenestration_action_id" not in st.session_state:
|
767 |
-
st.session_state.fenestration_action_id = None
|
768 |
|
769 |
def display_material_editor():
|
770 |
"""Display the material editor form."""
|
@@ -913,59 +895,53 @@ def display_material_editor():
|
|
913 |
|
914 |
# Handle form submission
|
915 |
if submit_button and not is_library:
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
926 |
|
927 |
-
|
928 |
-
|
929 |
-
|
|
|
|
|
|
|
|
|
|
|
930 |
else:
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
"embodied_carbon": embodied_carbon,
|
939 |
-
"cost": cost,
|
940 |
-
"absorptivity": absorptivity,
|
941 |
-
"emissivity": emissivity,
|
942 |
-
"colour": colour
|
943 |
-
}
|
944 |
-
|
945 |
-
# Handle edit mode
|
946 |
-
if editor_state["edit_mode"]:
|
947 |
-
original_name = editor_state["original_name"]
|
948 |
-
if original_name != name:
|
949 |
-
del st.session_state.project_data["materials"]["project"][original_name]
|
950 |
-
st.session_state.project_data["materials"]["project"][name] = material_data
|
951 |
-
st.success(f"Material '{name}' updated successfully.")
|
952 |
-
logger.info(f"Updated material '{name}' in project")
|
953 |
-
else:
|
954 |
-
st.session_state.project_data["materials"]["project"][name] = material_data
|
955 |
-
st.success(f"Material '{name}' added to your project.")
|
956 |
-
logger.info(f"Added new material '{name}' to project")
|
957 |
-
|
958 |
-
# Reset editor
|
959 |
-
reset_material_editor()
|
960 |
-
st.rerun()
|
961 |
|
962 |
# Handle clear button
|
963 |
if clear_button:
|
964 |
-
|
965 |
-
|
966 |
-
st.session_state.material_action_id = action_id
|
967 |
-
reset_material_editor()
|
968 |
-
st.rerun()
|
969 |
|
970 |
def display_fenestration_editor():
|
971 |
"""Display the fenestration editor form."""
|
@@ -1078,54 +1054,48 @@ def display_fenestration_editor():
|
|
1078 |
|
1079 |
# Handle form submission
|
1080 |
if submit_button and not is_library:
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1089 |
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
|
|
|
|
|
|
|
|
|
|
1093 |
else:
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
"embodied_carbon": embodied_carbon,
|
1102 |
-
"cost": cost
|
1103 |
-
}
|
1104 |
-
|
1105 |
-
# Handle edit mode
|
1106 |
-
if editor_state["edit_mode"]:
|
1107 |
-
original_name = editor_state["original_name"]
|
1108 |
-
if original_name != name:
|
1109 |
-
del st.session_state.project_data["fenestrations"]["project"][original_name]
|
1110 |
-
st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
|
1111 |
-
st.success(f"Fenestration '{name}' updated successfully.")
|
1112 |
-
logger.info(f"Updated fenestration '{name}' in project")
|
1113 |
-
else:
|
1114 |
-
st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
|
1115 |
-
st.success(f"Fenestration '{name}' added to your project.")
|
1116 |
-
logger.info(f"Added new fenestration '{name}' to project")
|
1117 |
-
|
1118 |
-
# Reset editor
|
1119 |
-
reset_fenestration_editor()
|
1120 |
-
st.rerun()
|
1121 |
|
1122 |
# Handle clear button
|
1123 |
if clear_button:
|
1124 |
-
|
1125 |
-
|
1126 |
-
st.session_state.fenestration_action_id = action_id
|
1127 |
-
reset_fenestration_editor()
|
1128 |
-
st.rerun()
|
1129 |
|
1130 |
def validate_material(
|
1131 |
name: str, category: str, thermal_conductivity: float, density: float,
|
@@ -1144,8 +1114,8 @@ def validate_material(
|
|
1144 |
|
1145 |
# Check for name uniqueness
|
1146 |
if not edit_mode or (edit_mode and name != original_name):
|
1147 |
-
if name in st.session_state.project_data["materials"]["project"]:
|
1148 |
-
errors.append(f"Material name '{name}' already exists in your project.")
|
1149 |
|
1150 |
# Validate category
|
1151 |
if category not in MATERIAL_CATEGORIES:
|
@@ -1216,8 +1186,8 @@ def validate_fenestration(
|
|
1216 |
|
1217 |
# Check for name uniqueness
|
1218 |
if not edit_mode or (edit_mode and name != original_name):
|
1219 |
-
if name in st.session_state.project_data["fenestrations"]["project"]:
|
1220 |
-
errors.append(f"Fenestration name '{name}' already exists in your project.")
|
1221 |
|
1222 |
# Validate type
|
1223 |
if fenestration_type not in FENESTRATION_TYPES:
|
@@ -1267,7 +1237,6 @@ def reset_material_editor():
|
|
1267 |
"original_name": "",
|
1268 |
"is_library": False
|
1269 |
}
|
1270 |
-
st.session_state.material_action_id = None
|
1271 |
|
1272 |
def reset_fenestration_editor():
|
1273 |
"""Reset the fenestration editor to default values."""
|
@@ -1284,7 +1253,6 @@ def reset_fenestration_editor():
|
|
1284 |
"original_name": "",
|
1285 |
"is_library": False
|
1286 |
}
|
1287 |
-
st.session_state.fenestration_action_id = None
|
1288 |
|
1289 |
def check_material_in_use(material_name: str) -> bool:
|
1290 |
"""
|
@@ -1322,7 +1290,7 @@ def display_materials_help():
|
|
1322 |
* **Thermal Conductivity (W/m·K)**: Rate of heat transfer through a material. Lower values indicate better insulation.
|
1323 |
* **Density (kg/m³)**: Mass per unit volume.
|
1324 |
* **Specific Heat (J/kg·K)**: Energy required to raise the temperature of 1 kg by 1 K. Higher values indicate better thermal mass.
|
1325 |
-
* **Thermal Mass (J/m²·K)
|
1326 |
* **U-Value (W/m²·K)**: Overall heat transfer coefficient, accounting for material and surface resistances. Lower values indicate better insulation.
|
1327 |
* **SHGC**: Solar Heat Gain Coefficient (0-1). Fraction of incident solar radiation that enters through a fenestration.
|
1328 |
* **Visible Transmittance**: Fraction of visible light that passes through a fenestration.
|
|
|
332 |
r_total = R_SI + r_material + R_SE
|
333 |
return 1 / r_total if r_total > 0 else 0.0
|
334 |
|
335 |
+
def calculate_thermal_mass(density: float, specific_heat: float, thickness: float) -> str:
|
336 |
+
"""Calculate areal thermal mass for a material and return category."""
|
337 |
+
thermal_mass = density * specific_heat * thickness
|
338 |
+
if thermal_mass < 30000:
|
339 |
+
return "Low"
|
340 |
+
elif 30000 <= thermal_mass <= 90000:
|
341 |
+
return "Medium"
|
342 |
+
else:
|
343 |
+
return "High"
|
344 |
|
345 |
def display_materials_page():
|
346 |
"""
|
|
|
402 |
if filtered_materials:
|
403 |
cols = st.columns([2, 1, 1, 1, 1])
|
404 |
cols[0].write("**Name**")
|
405 |
+
cols[1].write("**Thermal Mass**")
|
406 |
cols[2].write("**U-Value (W/m²·K)**")
|
407 |
cols[3].write("**Preview**")
|
408 |
cols[4].write("**Copy**")
|
|
|
413 |
thermal_mass = calculate_thermal_mass(material["density"], material["specific_heat"], material["default_thickness"])
|
414 |
u_value = calculate_u_value(material["thermal_conductivity"], material["default_thickness"])
|
415 |
cols[0].write(name)
|
416 |
+
cols[1].write(thermal_mass)
|
417 |
cols[2].write(f"{u_value:.3f}")
|
418 |
+
if cols[3].button("Preview", key=f"preview_lib_mat_{name}"):
|
419 |
+
st.session_state.material_editor = {
|
420 |
+
"name": name,
|
421 |
+
"category": material["category"],
|
422 |
+
"thermal_conductivity": material["thermal_conductivity"],
|
423 |
+
"density": material["density"],
|
424 |
+
"specific_heat": material["specific_heat"],
|
425 |
+
"default_thickness": material["default_thickness"],
|
426 |
+
"embodied_carbon": material["embodied_carbon"],
|
427 |
+
"cost": material["cost"],
|
428 |
+
"absorptivity": material["absorptivity"],
|
429 |
+
"emissivity": material["emissivity"],
|
430 |
+
"colour": material["colour"],
|
431 |
+
"edit_mode": False,
|
432 |
+
"original_name": name,
|
433 |
+
"is_library": True
|
434 |
+
}
|
435 |
+
st.session_state.active_tab = "Materials"
|
436 |
+
st.success(f"Previewing material '{name}'")
|
437 |
+
logger.info(f"Previewed library material '{name}'")
|
438 |
+
if cols[4].button("Copy", key=f"copy_lib_mat_{name}"):
|
439 |
+
new_name = f"{name}_Project"
|
440 |
+
counter = 1
|
441 |
+
while new_name in st.session_state.project_data["materials"]["project"] or new_name in library_materials:
|
442 |
+
new_name = f"{name}_Project_{counter}"
|
443 |
+
counter += 1
|
444 |
+
st.session_state.project_data["materials"]["project"][new_name] = material.copy()
|
445 |
+
st.success(f"Material '{new_name}' copied to project.")
|
446 |
+
logger.info(f"Copied library material '{name}' as '{new_name}' to project")
|
447 |
+
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
448 |
else:
|
449 |
st.info("No materials found in the selected category.")
|
450 |
|
|
|
462 |
if filtered_project_materials:
|
463 |
cols = st.columns([2, 1, 1, 1, 1])
|
464 |
cols[0].write("**Name**")
|
465 |
+
cols[1].write("**Thermal Mass**")
|
466 |
cols[2].write("**U-Value (W/m²·K)**")
|
467 |
cols[3].write("**Edit**")
|
468 |
cols[4].write("**Delete**")
|
|
|
473 |
thermal_mass = calculate_thermal_mass(material["density"], material["specific_heat"], material["default_thickness"])
|
474 |
u_value = calculate_u_value(material["thermal_conductivity"], material["default_thickness"])
|
475 |
cols[0].write(name)
|
476 |
+
cols[1].write(thermal_mass)
|
477 |
cols[2].write(f"{u_value:.3f}")
|
478 |
+
if cols[3].button("Edit", key=f"edit_proj_mat_{name}"):
|
479 |
+
st.session_state.material_editor = {
|
480 |
+
"name": name,
|
481 |
+
"category": material["category"],
|
482 |
+
"thermal_conductivity": material["thermal_conductivity"],
|
483 |
+
"density": material["density"],
|
484 |
+
"specific_heat": material["specific_heat"],
|
485 |
+
"default_thickness": material["default_thickness"],
|
486 |
+
"embodied_carbon": material["embodied_carbon"],
|
487 |
+
"cost": material["cost"],
|
488 |
+
"absorptivity": material["absorptivity"],
|
489 |
+
"emissivity": material["emissivity"],
|
490 |
+
"colour": material["colour"],
|
491 |
+
"edit_mode": True,
|
492 |
+
"original_name": name,
|
493 |
+
"is_library": False
|
494 |
+
}
|
495 |
+
st.session_state.active_tab = "Materials"
|
496 |
+
st.success(f"Editing material '{name}'")
|
497 |
+
logger.info(f"Editing project material '{name}'")
|
498 |
+
if cols[4].button("Delete", key=f"delete_proj_mat_{name}"):
|
499 |
+
is_in_use = check_material_in_use(name)
|
500 |
+
if is_in_use:
|
501 |
+
st.error(f"Cannot delete material '{name}' because it is in use in constructions.")
|
502 |
+
else:
|
503 |
+
del st.session_state.project_data["materials"]["project"][name]
|
504 |
+
st.success(f"Material '{name}' deleted from project.")
|
505 |
+
logger.info(f"Deleted material '{name}' from project")
|
506 |
+
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
507 |
else:
|
508 |
st.info("No project materials in the selected category.")
|
509 |
|
|
|
527 |
"Absorptivity": props["absorptivity"],
|
528 |
"Emissivity": props["emissivity"],
|
529 |
"Colour": props["colour"],
|
530 |
+
"Thermal Mass": thermal_mass,
|
531 |
"U-Value (W/m²·K)": u_value
|
532 |
})
|
533 |
df = pd.DataFrame(data)
|
|
|
577 |
cols[0].write(name)
|
578 |
cols[1].write(fenestration["type"])
|
579 |
cols[2].write(f"{fenestration['u_value']:.2f}")
|
580 |
+
if cols[3].button("Preview", key=f"preview_lib_fen_{name}"):
|
581 |
+
st.session_state.fenestration_editor = {
|
582 |
+
"name": name,
|
583 |
+
"type": fenestration["type"],
|
584 |
+
"u_value": fenestration["u_value"],
|
585 |
+
"shgc": fenestration["shgc"],
|
586 |
+
"visible_transmittance": fenestration["visible_transmittance"],
|
587 |
+
"thickness": fenestration["thickness"],
|
588 |
+
"embodied_carbon": fenestration["embodied_carbon"],
|
589 |
+
"cost": fenestration["cost"],
|
590 |
+
"edit_mode": False,
|
591 |
+
"original_name": name,
|
592 |
+
"is_library": True
|
593 |
+
}
|
594 |
+
st.session_state.active_tab = "Fenestrations"
|
595 |
+
st.success(f"Previewing fenestration '{name}'")
|
596 |
+
logger.info(f"Previewed library fenestration '{name}'")
|
597 |
+
if cols[4].button("Copy", key=f"copy_lib_fen_{name}"):
|
598 |
+
new_name = f"{name}_Project"
|
599 |
+
counter = 1
|
600 |
+
while new_name in st.session_state.project_data["fenestrations"]["project"] or new_name in library_fenestrations:
|
601 |
+
new_name = f"{name}_Project_{counter}"
|
602 |
+
counter += 1
|
603 |
+
st.session_state.project_data["fenestrations"]["project"][new_name] = fenestration.copy()
|
604 |
+
st.success(f"Fenestration '{new_name}' copied to project.")
|
605 |
+
logger.info(f"Copied library fenestration '{name}' as '{new_name}' to project")
|
606 |
+
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
607 |
else:
|
608 |
st.info("No fenestrations found in the selected type.")
|
609 |
|
|
|
632 |
cols[0].write(name)
|
633 |
cols[1].write(fenestration["type"])
|
634 |
cols[2].write(f"{fenestration['u_value']:.2f}")
|
635 |
+
if cols[3].button("Edit", key=f"edit_proj_fen_{name}"):
|
636 |
+
st.session_state.fenestration_editor = {
|
637 |
+
"name": name,
|
638 |
+
"type": fenestration["type"],
|
639 |
+
"u_value": fenestration["u_value"],
|
640 |
+
"shgc": fenestration["shgc"],
|
641 |
+
"visible_transmittance": fenestration["visible_transmittance"],
|
642 |
+
"thickness": fenestration["thickness"],
|
643 |
+
"embodied_carbon": fenestration["embodied_carbon"],
|
644 |
+
"cost": fenestration["cost"],
|
645 |
+
"edit_mode": True,
|
646 |
+
"original_name": name,
|
647 |
+
"is_library": False
|
648 |
+
}
|
649 |
+
st.session_state.active_tab = "Fenestrations"
|
650 |
+
st.success(f"Editing fenestration '{name}'")
|
651 |
+
logger.info(f"Editing project fenestration '{name}'")
|
652 |
+
if cols[4].button("Delete", key=f"delete_proj_fen_{name}"):
|
653 |
+
is_in_use = check_fenestration_in_use(name)
|
654 |
+
if is_in_use:
|
655 |
+
st.error(f"Cannot delete fenestration '{name}' because it is in use in components.")
|
656 |
+
else:
|
657 |
+
del st.session_state.project_data["fenestrations"]["project"][name]
|
658 |
+
st.success(f"Fenestration '{name}' deleted from project.")
|
659 |
+
logger.info(f"Deleted fenestration '{name}' from project")
|
660 |
+
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
661 |
else:
|
662 |
st.info("No project fenestrations in the selected type.")
|
663 |
|
|
|
718 |
"original_name": "",
|
719 |
"is_library": False
|
720 |
}
|
|
|
|
|
721 |
|
722 |
def initialize_fenestrations():
|
723 |
"""Initialize fenestrations in session state if not present."""
|
|
|
747 |
"original_name": "",
|
748 |
"is_library": False
|
749 |
}
|
|
|
|
|
750 |
|
751 |
def display_material_editor():
|
752 |
"""Display the material editor form."""
|
|
|
895 |
|
896 |
# Handle form submission
|
897 |
if submit_button and not is_library:
|
898 |
+
# Validate inputs
|
899 |
+
validation_errors = validate_material(
|
900 |
+
name, category, thermal_conductivity, density, specific_heat,
|
901 |
+
default_thickness, embodied_carbon, cost,
|
902 |
+
editor_state["edit_mode"], editor_state["original_name"],
|
903 |
+
absorptivity, emissivity, colour
|
904 |
+
)
|
905 |
+
|
906 |
+
if validation_errors:
|
907 |
+
for error in validation_errors:
|
908 |
+
st.error(error)
|
909 |
+
else:
|
910 |
+
# Create material data
|
911 |
+
material_data = {
|
912 |
+
"category": category,
|
913 |
+
"thermal_conductivity": thermal_conductivity,
|
914 |
+
"density": density,
|
915 |
+
"specific_heat": specific_heat,
|
916 |
+
"default_thickness": default_thickness,
|
917 |
+
"embodied_carbon": embodied_carbon,
|
918 |
+
"cost": cost,
|
919 |
+
"absorptivity": absorptivity,
|
920 |
+
"emissivity": emissivity,
|
921 |
+
"colour": colour
|
922 |
+
}
|
923 |
|
924 |
+
# Handle edit mode
|
925 |
+
if editor_state["edit_mode"]:
|
926 |
+
original_name = editor_state["original_name"]
|
927 |
+
if original_name != name:
|
928 |
+
del st.session_state.project_data["materials"]["project"][original_name]
|
929 |
+
st.session_state.project_data["materials"]["project"][name] = material_data
|
930 |
+
st.success(f"Material '{name}' updated successfully.")
|
931 |
+
logger.info(f"Updated material '{name}' in project")
|
932 |
else:
|
933 |
+
st.session_state.project_data["materials"]["project"][name] = material_data
|
934 |
+
st.success(f"Material '{name}' added to your project.")
|
935 |
+
logger.info(f"Added new material '{name}' to project")
|
936 |
+
|
937 |
+
# Reset editor
|
938 |
+
reset_material_editor()
|
939 |
+
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
940 |
|
941 |
# Handle clear button
|
942 |
if clear_button:
|
943 |
+
reset_material_editor()
|
944 |
+
st.rerun()
|
|
|
|
|
|
|
945 |
|
946 |
def display_fenestration_editor():
|
947 |
"""Display the fenestration editor form."""
|
|
|
1054 |
|
1055 |
# Handle form submission
|
1056 |
if submit_button and not is_library:
|
1057 |
+
# Validate inputs
|
1058 |
+
validation_errors = validate_fenestration(
|
1059 |
+
name, fenestration_type, u_value, shgc, visible_transmittance, thickness,
|
1060 |
+
embodied_carbon, cost, editor_state["edit_mode"], editor_state["original_name"]
|
1061 |
+
)
|
1062 |
+
|
1063 |
+
if validation_errors:
|
1064 |
+
for error in validation_errors:
|
1065 |
+
st.error(error)
|
1066 |
+
else:
|
1067 |
+
# Create fenestration data
|
1068 |
+
fenestration_data = {
|
1069 |
+
"type": fenestration_type,
|
1070 |
+
"u_value": u_value,
|
1071 |
+
"shgc": shgc,
|
1072 |
+
"visible_transmittance": visible_transmittance,
|
1073 |
+
"thickness": thickness,
|
1074 |
+
"embodied_carbon": embodied_carbon,
|
1075 |
+
"cost": cost
|
1076 |
+
}
|
1077 |
|
1078 |
+
# Handle edit mode
|
1079 |
+
if editor_state["edit_mode"]:
|
1080 |
+
original_name = editor_state["original_name"]
|
1081 |
+
if original_name != name:
|
1082 |
+
del st.session_state.project_data["fenestrations"]["project"][original_name]
|
1083 |
+
st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
|
1084 |
+
st.success(f"Fenestration '{name}' updated successfully.")
|
1085 |
+
logger.info(f"Updated fenestration '{name}' in project")
|
1086 |
else:
|
1087 |
+
st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
|
1088 |
+
st.success(f"Fenestration '{name}' added to your project.")
|
1089 |
+
logger.info(f"Added new fenestration '{name}' to project")
|
1090 |
+
|
1091 |
+
# Reset editor
|
1092 |
+
reset_fenestration_editor()
|
1093 |
+
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1094 |
|
1095 |
# Handle clear button
|
1096 |
if clear_button:
|
1097 |
+
reset_fenestration_editor()
|
1098 |
+
st.rerun()
|
|
|
|
|
|
|
1099 |
|
1100 |
def validate_material(
|
1101 |
name: str, category: str, thermal_conductivity: float, density: float,
|
|
|
1114 |
|
1115 |
# Check for name uniqueness
|
1116 |
if not edit_mode or (edit_mode and name != original_name):
|
1117 |
+
if name in st.session_state.project_data["materials"]["project"] or name in st.session_state.project_data["materials"]["library"]:
|
1118 |
+
errors.append(f"Material name '{name}' already exists in your project or library.")
|
1119 |
|
1120 |
# Validate category
|
1121 |
if category not in MATERIAL_CATEGORIES:
|
|
|
1186 |
|
1187 |
# Check for name uniqueness
|
1188 |
if not edit_mode or (edit_mode and name != original_name):
|
1189 |
+
if name in st.session_state.project_data["fenestrations"]["project"] or name in st.session_state.project_data["fenestrations"]["library"]:
|
1190 |
+
errors.append(f"Fenestration name '{name}' already exists in your project or library.")
|
1191 |
|
1192 |
# Validate type
|
1193 |
if fenestration_type not in FENESTRATION_TYPES:
|
|
|
1237 |
"original_name": "",
|
1238 |
"is_library": False
|
1239 |
}
|
|
|
1240 |
|
1241 |
def reset_fenestration_editor():
|
1242 |
"""Reset the fenestration editor to default values."""
|
|
|
1253 |
"original_name": "",
|
1254 |
"is_library": False
|
1255 |
}
|
|
|
1256 |
|
1257 |
def check_material_in_use(material_name: str) -> bool:
|
1258 |
"""
|
|
|
1290 |
* **Thermal Conductivity (W/m·K)**: Rate of heat transfer through a material. Lower values indicate better insulation.
|
1291 |
* **Density (kg/m³)**: Mass per unit volume.
|
1292 |
* **Specific Heat (J/kg·K)**: Energy required to raise the temperature of 1 kg by 1 K. Higher values indicate better thermal mass.
|
1293 |
+
* **Thermal Mass**: Areal heat capacity, categorized as Low (<30,000 J/m²·K), Medium (30,000–90,000 J/m²·K), or High (>90,000 J/m²·K).
|
1294 |
* **U-Value (W/m²·K)**: Overall heat transfer coefficient, accounting for material and surface resistances. Lower values indicate better insulation.
|
1295 |
* **SHGC**: Solar Heat Gain Coefficient (0-1). Fraction of incident solar radiation that enters through a fenestration.
|
1296 |
* **Visible Transmittance**: Fraction of visible light that passes through a fenestration.
|