mabuseif commited on
Commit
5615cd1
·
verified ·
1 Parent(s): b051419

Update app/materials_library.py

Browse files
Files changed (1) hide show
  1. app/materials_library.py +361 -402
app/materials_library.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- BuildSustain - Material Library Module (Final Fix)
3
 
4
  This module handles the material library functionality of the BuildSustain application,
5
  allowing users to manage building materials and fenestrations (windows, doors, skylights).
@@ -359,62 +359,41 @@ def display_materials_page():
359
  Display the material library page.
360
  This is the main function called by main.py when the Material Library page is selected.
361
  """
362
- # Prevent multiple renders by checking if we're already processing
363
- if "materials_page_rendering" in st.session_state and st.session_state.materials_page_rendering:
364
- return
 
 
 
 
365
 
366
- # Set rendering flag
367
- st.session_state.materials_page_rendering = True
 
368
 
369
- try:
370
- # Initialize session state flags only once
371
- if "material_saved" not in st.session_state:
372
- st.session_state.material_saved = False
373
- if "fenestration_saved" not in st.session_state:
374
- st.session_state.fenestration_saved = False
375
-
376
- st.title("Material Library")
377
-
378
- # Display help information in an expandable section
379
- with st.expander("Help & Information"):
380
- display_materials_help()
381
-
382
- # Create tabs for materials and fenestrations
383
- tab1, tab2 = st.tabs(["Materials", "Fenestrations"])
384
-
385
- # Materials tab
386
- with tab1:
387
- display_materials_tab()
388
-
389
- # Fenestrations tab
390
- with tab2:
391
- display_fenestrations_tab()
392
-
393
- # Navigation buttons
394
- col1, col2 = st.columns(2)
395
-
396
- with col1:
397
- if st.button("Back to Climate Data", key="back_to_climate"):
398
- st.session_state.current_page = "Climate Data"
399
- st.session_state.materials_page_rendering = False
400
- st.rerun()
401
-
402
- with col2:
403
- if st.button("Continue to Construction", key="continue_to_construction"):
404
- st.session_state.current_page = "Construction"
405
- st.session_state.materials_page_rendering = False
406
- st.rerun()
407
-
408
- # Handle material/fenestration saved flags - trigger rerun only once
409
- if st.session_state.material_saved or st.session_state.fenestration_saved:
410
- st.session_state.material_saved = False
411
- st.session_state.fenestration_saved = False
412
- st.session_state.materials_page_rendering = False
413
  st.rerun()
414
-
415
- finally:
416
- # Always clear the rendering flag
417
- st.session_state.materials_page_rendering = False
418
 
419
  def display_materials_tab():
420
  """Display the materials tab content."""
@@ -489,7 +468,6 @@ def display_materials_tab():
489
  st.session_state.project_data["materials"]["project"][new_name] = material.copy()
490
  st.success(f"Material '{new_name}' copied to project.")
491
  logger.info(f"Copied library material '{name}' as '{new_name}' to project")
492
- st.session_state.materials_page_rendering = False
493
  st.rerun()
494
  else:
495
  st.info("No materials found in the selected category.")
@@ -555,7 +533,6 @@ def display_materials_tab():
555
  del st.session_state.project_data["materials"]["project"][name]
556
  st.success(f"Material '{name}' deleted from project.")
557
  logger.info(f"Deleted material '{name}' from project")
558
- st.session_state.materials_page_rendering = False
559
  st.rerun()
560
  else:
561
  st.info("No project materials in the selected category.")
@@ -662,7 +639,6 @@ def display_fenestrations_tab():
662
  st.session_state.project_data["fenestrations"]["project"][new_name] = fenestration.copy()
663
  st.success(f"Fenestration '{new_name}' copied to project.")
664
  logger.info(f"Copied library fenestration '{name}' as '{new_name}' to project")
665
- st.session_state.materials_page_rendering = False
666
  st.rerun()
667
  else:
668
  st.info("No fenestrations found in the selected type.")
@@ -722,7 +698,6 @@ def display_fenestrations_tab():
722
  del st.session_state.project_data["fenestrations"]["project"][name]
723
  st.success(f"Fenestration '{name}' deleted from project.")
724
  logger.info(f"Deleted fenestration '{name}' from project")
725
- st.session_state.materials_page_rendering = False
726
  st.rerun()
727
  else:
728
  st.info("No project fenestrations in the selected type.")
@@ -816,372 +791,356 @@ def initialize_fenestrations():
816
 
817
  def display_material_editor():
818
  """Display the material editor form."""
819
- # Prevent form from being rendered multiple times
820
- if "material_form_rendered" in st.session_state and st.session_state.material_form_rendered:
821
- return
822
-
823
- st.session_state.material_form_rendered = True
824
-
825
- try:
826
- with st.form("material_editor_form"):
827
- editor_state = st.session_state.material_editor
828
- is_library = editor_state.get("is_library", False)
829
-
830
- # Material name
831
- name = st.text_input(
832
- "Material Name",
833
- value=editor_state["name"],
834
- help="Enter a unique name for the material.",
 
 
 
 
 
 
835
  disabled=is_library
836
  )
837
 
838
- # Create two columns for layout
839
- col1, col2 = st.columns(2)
840
-
841
- with col1:
842
- # Category
843
- category = st.selectbox(
844
- "Category",
845
- MATERIAL_CATEGORIES,
846
- index=MATERIAL_CATEGORIES.index(editor_state["category"]) if editor_state["category"] in MATERIAL_CATEGORIES else 0,
847
- help="Select the material category.",
848
- disabled=is_library
849
- )
850
-
851
- # Thermal conductivity
852
- thermal_conductivity = st.number_input(
853
- "Thermal Conductivity (W/m·K)",
854
- min_value=0.001,
855
- max_value=1000.0,
856
- value=float(editor_state["thermal_conductivity"]),
857
- format="%.3f",
858
- help="Thermal conductivity in W/m·K. Lower values indicate better insulation.",
859
- disabled=is_library
860
- )
861
-
862
- # Density
863
- density = st.number_input(
864
- "Density (kg/m³)",
865
- min_value=1.0,
866
- max_value=20000.0,
867
- value=float(editor_state["density"]),
868
- format="%.1f",
869
- help="Material density in kg/m³.",
870
- disabled=is_library
871
- )
872
-
873
- with col2:
874
- # Specific heat
875
- specific_heat = st.number_input(
876
- "Specific Heat (J/kg·K)",
877
- min_value=100.0,
878
- max_value=10000.0,
879
- value=float(editor_state["specific_heat"]),
880
- format="%.1f",
881
- help="Specific heat capacity in J/kg·K. Higher values indicate better thermal mass.",
882
- disabled=is_library
883
- )
884
-
885
- # Default thickness
886
- default_thickness = st.number_input(
887
- "Thickness (m)",
888
- min_value=0.001,
889
- max_value=0.5,
890
- value=float(editor_state["default_thickness"]),
891
- format="%.3f",
892
- help="Thickness for this material in meters.",
893
- disabled=is_library
894
- )
895
-
896
- # Absorptivity
897
- absorptivity = st.number_input(
898
- "Absorptivity",
899
- min_value=0.0,
900
- max_value=1.0,
901
- value=float(editor_state["absorptivity"]),
902
- format="%.2f",
903
- help="Solar radiation absorbed (0-1).",
904
- disabled=is_library
905
- )
906
-
907
- # Other properties
908
- col1, col2 = st.columns(2)
909
-
910
- with col1:
911
- # Emissivity
912
- emissivity = st.number_input(
913
- "Emissivity",
914
- min_value=0.0,
915
- max_value=1.0,
916
- value=float(editor_state["emissivity"]),
917
- format="%.2f",
918
- help="Ratio of radiation emitted (0-1).",
919
- disabled=is_library
920
- )
921
-
922
- with col2:
923
- # Colour
924
- colour = st.selectbox(
925
- "Colour Category",
926
- COLOUR_CATEGORIES,
927
- index=COLOUR_CATEGORIES.index(editor_state["colour"]) if editor_state["colour"] in COLOUR_CATEGORIES else 0,
928
- help="Colour category affecting absorptivity.",
929
- disabled=is_library
930
- )
931
-
932
- # Additional properties
933
- st.subheader("Additional Properties")
934
- col1, col2 = st.columns(2)
935
-
936
- with col1:
937
- # Embodied carbon
938
- embodied_carbon = st.number_input(
939
- "Embodied Carbon (kg CO₂e/m³)",
940
- min_value=0.0,
941
- max_value=10000.0,
942
- value=float(editor_state["embodied_carbon"]),
943
- format="%.1f",
944
- help="Embodied carbon in kg CO₂e per cubic meter.",
945
- disabled=is_library
946
- )
947
-
948
- with col2:
949
- # Cost
950
- cost = st.number_input(
951
- "Cost (USD/m³)",
952
- min_value=0.0,
953
- max_value=10000.0,
954
- value=float(editor_state["cost"]),
955
- format="%.1f",
956
- help="Material cost in USD per cubic meter.",
957
- disabled=is_library
958
- )
959
 
960
- # Form submission buttons
961
- col1, col2 = st.columns(2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
962
 
963
- with col1:
964
- submit_button = st.form_submit_button("Save Material", disabled=is_library)
 
 
 
 
 
 
 
 
965
 
966
- with col2:
967
- clear_button = st.form_submit_button("Clear Form")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
968
 
969
- # Handle form submission
970
- if submit_button and not is_library:
971
- # Validate inputs
972
- validation_errors = validate_material(
973
- name, category, thermal_conductivity, density, specific_heat,
974
- default_thickness, embodied_carbon, cost,
975
- editor_state["edit_mode"], editor_state["original_name"],
976
- absorptivity, emissivity, colour
977
- )
 
 
 
 
 
 
 
 
978
 
979
- if validation_errors:
980
- for error in validation_errors:
981
- st.error(error)
 
 
 
 
 
982
  else:
983
- # Create material data
984
- material_data = {
985
- "category": category,
986
- "thermal_conductivity": thermal_conductivity,
987
- "density": density,
988
- "specific_heat": specific_heat,
989
- "default_thickness": default_thickness,
990
- "embodied_carbon": embodied_carbon,
991
- "cost": cost,
992
- "absorptivity": absorptivity,
993
- "emissivity": emissivity,
994
- "colour": colour
995
- }
996
-
997
- # Handle edit mode
998
- if editor_state["edit_mode"]:
999
- original_name = editor_state["original_name"]
1000
- if original_name != name:
1001
- del st.session_state.project_data["materials"]["project"][original_name]
1002
- st.session_state.project_data["materials"]["project"][name] = material_data
1003
- st.success(f"Material '{name}' updated successfully.")
1004
- logger.info(f"Updated material '{name}' in project")
1005
- else:
1006
- st.session_state.project_data["materials"]["project"][name] = material_data
1007
- st.success(f"Material '{name}' added to your project.")
1008
- logger.info(f"Added new material '{name}' to project")
1009
-
1010
- # Reset editor and flag for rerun
1011
- reset_material_editor()
1012
- st.session_state.material_saved = True
1013
-
1014
- # Handle clear button
1015
- if clear_button:
1016
- reset_material_editor()
1017
- st.session_state.material_saved = True
1018
 
1019
- finally:
1020
- st.session_state.material_form_rendered = False
 
 
 
 
 
 
 
 
1021
 
1022
  def display_fenestration_editor():
1023
  """Display the fenestration editor form."""
1024
- # Prevent form from being rendered multiple times
1025
- if "fenestration_form_rendered" in st.session_state and st.session_state.fenestration_form_rendered:
1026
- return
1027
-
1028
- st.session_state.fenestration_form_rendered = True
1029
-
1030
- try:
1031
- with st.form("fenestration_editor_form"):
1032
- editor_state = st.session_state.fenestration_editor
1033
- is_library = editor_state.get("is_library", False)
1034
-
1035
- # Fenestration name
1036
- name = st.text_input(
1037
- "Fenestration Name",
1038
- value=editor_state["name"],
1039
- help="Enter a unique name for the fenestration.",
 
 
 
 
 
 
1040
  disabled=is_library
1041
  )
1042
 
1043
- # Create two columns for layout
1044
- col1, col2 = st.columns(2)
1045
-
1046
- with col1:
1047
- # Type
1048
- fenestration_type = st.selectbox(
1049
- "Type",
1050
- FENESTRATION_TYPES,
1051
- index=FENESTRATION_TYPES.index(editor_state["type"]) if editor_state["type"] in FENESTRATION_TYPES else 0,
1052
- help="Select the fenestration type.",
1053
- disabled=is_library
1054
- )
1055
-
1056
- # U-value
1057
- u_value = st.number_input(
1058
- "U-Value (W/m²·K)",
1059
- min_value=0.1,
1060
- max_value=10.0,
1061
- value=float(editor_state["u_value"]),
1062
- format="%.2f",
1063
- help="U-value in W/m²·K. Lower values indicate better insulation.",
1064
- disabled=is_library
1065
- )
1066
-
1067
- # SHGC
1068
- shgc = st.number_input(
1069
- "Solar Heat Gain Coefficient (SHGC)",
1070
- min_value=0.0,
1071
- max_value=1.0,
1072
- value=float(editor_state["shgc"]),
1073
- format="%.2f",
1074
- help="Solar Heat Gain Coefficient (0-1). Lower values indicate less solar heat transmission.",
1075
- disabled=is_library
1076
- )
1077
-
1078
- with col2:
1079
- # Visible transmittance
1080
- visible_transmittance = st.number_input(
1081
- "Visible Transmittance",
1082
- min_value=0.0,
1083
- max_value=1.0,
1084
- value=float(editor_state["visible_transmittance"]),
1085
- format="%.2f",
1086
- help="Visible Transmittance (0-1). Higher values indicate more visible light transmission.",
1087
- disabled=is_library
1088
- )
1089
-
1090
- # Thickness
1091
- thickness = st.number_input(
1092
- "Thickness (m)",
1093
- min_value=0.001,
1094
- max_value=0.1,
1095
- value=float(editor_state["thickness"]),
1096
- format="%.3f",
1097
- help="Thickness in meters.",
1098
- disabled=is_library
1099
- )
1100
-
1101
- # Additional properties
1102
- st.subheader("Additional Properties")
1103
- col1, col2 = st.columns(2)
1104
-
1105
- with col1:
1106
- # Embodied carbon
1107
- embodied_carbon = st.number_input(
1108
- "Embodied Carbon (kg CO₂e/m²)",
1109
- min_value=0.0,
1110
- max_value=5000.0,
1111
- value=float(editor_state["embodied_carbon"]),
1112
- format="%.1f",
1113
- help="Embodied carbon in kg CO₂e per square meter.",
1114
- disabled=is_library
1115
- )
1116
-
1117
- with col2:
1118
- # Cost
1119
- cost = st.number_input(
1120
- "Cost (USD/m²)",
1121
- min_value=0.0,
1122
- max_value=5000.0,
1123
- value=float(editor_state["cost"]),
1124
- format="%.1f",
1125
- help="Fenestration cost in USD per square meter.",
1126
- disabled=is_library
1127
- )
1128
-
1129
- # Form submission buttons
1130
- col1, col2 = st.columns(2)
1131
 
1132
- with col1:
1133
- submit_button = st.form_submit_button("Save Fenestration", disabled=is_library)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1134
 
1135
- with col2:
1136
- clear_button = st.form_submit_button("Clear Form")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1137
 
1138
- # Handle form submission
1139
- if submit_button and not is_library:
1140
- # Validate inputs
1141
- validation_errors = validate_fenestration(
1142
- name, fenestration_type, u_value, shgc, visible_transmittance, thickness,
1143
- embodied_carbon, cost, editor_state["edit_mode"], editor_state["original_name"]
1144
- )
 
 
 
 
 
 
 
1145
 
1146
- if validation_errors:
1147
- for error in validation_errors:
1148
- st.error(error)
 
 
 
 
 
1149
  else:
1150
- # Create fenestration data
1151
- fenestration_data = {
1152
- "type": fenestration_type,
1153
- "u_value": u_value,
1154
- "shgc": shgc,
1155
- "visible_transmittance": visible_transmittance,
1156
- "thickness": thickness,
1157
- "embodied_carbon": embodied_carbon,
1158
- "cost": cost
1159
- }
1160
-
1161
- # Handle edit mode
1162
- if editor_state["edit_mode"]:
1163
- original_name = editor_state["original_name"]
1164
- if original_name != name:
1165
- del st.session_state.project_data["fenestrations"]["project"][original_name]
1166
- st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
1167
- st.success(f"Fenestration '{name}' updated successfully.")
1168
- logger.info(f"Updated fenestration '{name}' in project")
1169
- else:
1170
- st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
1171
- st.success(f"Fenestration '{name}' added to your project.")
1172
- logger.info(f"Added new fenestration '{name}' to project")
1173
-
1174
- # Reset editor and flag for rerun
1175
- reset_fenestration_editor()
1176
- st.session_state.fenestration_saved = True
1177
-
1178
- # Handle clear button
1179
- if clear_button:
1180
- reset_fenestration_editor()
1181
- st.session_state.fenestration_saved = True
1182
 
1183
- finally:
1184
- st.session_state.fenestration_form_rendered = False
 
 
 
 
 
 
 
 
1185
 
1186
  def validate_material(
1187
  name: str, category: str, thermal_conductivity: float, density: float,
 
1
  """
2
+ BuildSustain - Material Library Module
3
 
4
  This module handles the material library functionality of the BuildSustain application,
5
  allowing users to manage building materials and fenestrations (windows, doors, skylights).
 
359
  Display the material library page.
360
  This is the main function called by main.py when the Material Library page is selected.
361
  """
362
+ # Initialize session state flags
363
+ if "material_saved" not in st.session_state:
364
+ st.session_state.material_saved = False
365
+ if "fenestration_saved" not in st.session_state:
366
+ st.session_state.fenestration_saved = False
367
+
368
+ st.title("Material Library")
369
 
370
+ # Display help information in an expandable section
371
+ with st.expander("Help & Information"):
372
+ display_materials_help()
373
 
374
+ # Create tabs for materials and fenestrations
375
+ tab1, tab2 = st.tabs(["Materials", "Fenestrations"])
376
+
377
+ # Materials tab
378
+ with tab1:
379
+ display_materials_tab()
380
+
381
+ # Fenestrations tab
382
+ with tab2:
383
+ display_fenestrations_tab()
384
+
385
+ # Navigation buttons
386
+ col1, col2 = st.columns(2)
387
+
388
+ with col1:
389
+ if st.button("Back to Climate Data", key="back_to_climate"):
390
+ st.session_state.current_page = "Climate Data"
391
+ st.rerun()
392
+
393
+ with col2:
394
+ if st.button("Continue to Construction", key="continue_to_construction"):
395
+ st.session_state.current_page = "Construction"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  st.rerun()
 
 
 
 
397
 
398
  def display_materials_tab():
399
  """Display the materials tab content."""
 
468
  st.session_state.project_data["materials"]["project"][new_name] = material.copy()
469
  st.success(f"Material '{new_name}' copied to project.")
470
  logger.info(f"Copied library material '{name}' as '{new_name}' to project")
 
471
  st.rerun()
472
  else:
473
  st.info("No materials found in the selected category.")
 
533
  del st.session_state.project_data["materials"]["project"][name]
534
  st.success(f"Material '{name}' deleted from project.")
535
  logger.info(f"Deleted material '{name}' from project")
 
536
  st.rerun()
537
  else:
538
  st.info("No project materials in the selected category.")
 
639
  st.session_state.project_data["fenestrations"]["project"][new_name] = fenestration.copy()
640
  st.success(f"Fenestration '{new_name}' copied to project.")
641
  logger.info(f"Copied library fenestration '{name}' as '{new_name}' to project")
 
642
  st.rerun()
643
  else:
644
  st.info("No fenestrations found in the selected type.")
 
698
  del st.session_state.project_data["fenestrations"]["project"][name]
699
  st.success(f"Fenestration '{name}' deleted from project.")
700
  logger.info(f"Deleted fenestration '{name}' from project")
 
701
  st.rerun()
702
  else:
703
  st.info("No project fenestrations in the selected type.")
 
791
 
792
  def display_material_editor():
793
  """Display the material editor form."""
794
+ with st.form("material_editor_form"):
795
+ editor_state = st.session_state.material_editor
796
+ is_library = editor_state.get("is_library", False)
797
+
798
+ # Material name
799
+ name = st.text_input(
800
+ "Material Name",
801
+ value=editor_state["name"],
802
+ help="Enter a unique name for the material.",
803
+ disabled=is_library
804
+ )
805
+
806
+ # Create two columns for layout
807
+ col1, col2 = st.columns(2)
808
+
809
+ with col1:
810
+ # Category
811
+ category = st.selectbox(
812
+ "Category",
813
+ MATERIAL_CATEGORIES,
814
+ index=MATERIAL_CATEGORIES.index(editor_state["category"]) if editor_state["category"] in MATERIAL_CATEGORIES else 0,
815
+ help="Select the material category.",
816
  disabled=is_library
817
  )
818
 
819
+ # Thermal conductivity
820
+ thermal_conductivity = st.number_input(
821
+ "Thermal Conductivity (W/m·K)",
822
+ min_value=0.001,
823
+ max_value=1000.0,
824
+ value=float(editor_state["thermal_conductivity"]),
825
+ format="%.3f",
826
+ help="Thermal conductivity in W/m·K. Lower values indicate better insulation.",
827
+ disabled=is_library
828
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
829
 
830
+ # Density
831
+ density = st.number_input(
832
+ "Density (kg/m³)",
833
+ min_value=1.0,
834
+ max_value=20000.0,
835
+ value=float(editor_state["density"]),
836
+ format="%.1f",
837
+ help="Material density in kg/m³.",
838
+ disabled=is_library
839
+ )
840
+
841
+ with col2:
842
+ # Specific heat
843
+ specific_heat = st.number_input(
844
+ "Specific Heat (J/kg·K)",
845
+ min_value=100.0,
846
+ max_value=10000.0,
847
+ value=float(editor_state["specific_heat"]),
848
+ format="%.1f",
849
+ help="Specific heat capacity in J/kg·K. Higher values indicate better thermal mass.",
850
+ disabled=is_library
851
+ )
852
 
853
+ # Default thickness
854
+ default_thickness = st.number_input(
855
+ "Thickness (m)",
856
+ min_value=0.001,
857
+ max_value=0.5,
858
+ value=float(editor_state["default_thickness"]),
859
+ format="%.3f",
860
+ help="Thickness for this material in meters.",
861
+ disabled=is_library
862
+ )
863
 
864
+ # Absorptivity
865
+ absorptivity = st.number_input(
866
+ "Absorptivity",
867
+ min_value=0.0,
868
+ max_value=1.0,
869
+ value=float(editor_state["absorptivity"]),
870
+ format="%.2f",
871
+ help="Solar radiation absorbed (0-1).",
872
+ disabled=is_library
873
+ )
874
+
875
+ # Other properties
876
+ col1, col2 = st.columns(2)
877
+
878
+ with col1:
879
+ # Emissivity
880
+ emissivity = st.number_input(
881
+ "Emissivity",
882
+ min_value=0.0,
883
+ max_value=1.0,
884
+ value=float(editor_state["emissivity"]),
885
+ format="%.2f",
886
+ help="Ratio of radiation emitted (0-1).",
887
+ disabled=is_library
888
+ )
889
+
890
+ with col2:
891
+ # Colour
892
+ colour = st.selectbox(
893
+ "Colour Category",
894
+ COLOUR_CATEGORIES,
895
+ index=COLOUR_CATEGORIES.index(editor_state["colour"]) if editor_state["colour"] in COLOUR_CATEGORIES else 0,
896
+ help="Colour category affecting absorptivity.",
897
+ disabled=is_library
898
+ )
899
+
900
+ # Additional properties
901
+ st.subheader("Additional Properties")
902
+ col1, col2 = st.columns(2)
903
+
904
+ with col1:
905
+ # Embodied carbon
906
+ embodied_carbon = st.number_input(
907
+ "Embodied Carbon (kg CO₂e/m³)",
908
+ min_value=0.0,
909
+ max_value=10000.0,
910
+ value=float(editor_state["embodied_carbon"]),
911
+ format="%.1f",
912
+ help="Embodied carbon in kg CO₂e per cubic meter.",
913
+ disabled=is_library
914
+ )
915
+
916
+ with col2:
917
+ # Cost
918
+ cost = st.number_input(
919
+ "Cost (USD/m³)",
920
+ min_value=0.0,
921
+ max_value=10000.0,
922
+ value=float(editor_state["cost"]),
923
+ format="%.1f",
924
+ help="Material cost in USD per cubic meter.",
925
+ disabled=is_library
926
+ )
927
+
928
+ # Form submission buttons
929
+ col1, col2 = st.columns(2)
930
+
931
+ with col1:
932
+ submit_button = st.form_submit_button("Save Material", disabled=is_library)
933
+
934
+ with col2:
935
+ clear_button = st.form_submit_button("Clear Form")
936
+
937
+ # Handle form submission
938
+ if submit_button and not is_library:
939
+ # Validate inputs
940
+ validation_errors = validate_material(
941
+ name, category, thermal_conductivity, density, specific_heat,
942
+ default_thickness, embodied_carbon, cost,
943
+ editor_state["edit_mode"], editor_state["original_name"],
944
+ absorptivity, emissivity, colour
945
+ )
946
 
947
+ if validation_errors:
948
+ for error in validation_errors:
949
+ st.error(error)
950
+ else:
951
+ # Create material data
952
+ material_data = {
953
+ "category": category,
954
+ "thermal_conductivity": thermal_conductivity,
955
+ "density": density,
956
+ "specific_heat": specific_heat,
957
+ "default_thickness": default_thickness,
958
+ "embodied_carbon": embodied_carbon,
959
+ "cost": cost,
960
+ "absorptivity": absorptivity,
961
+ "emissivity": emissivity,
962
+ "colour": colour
963
+ }
964
 
965
+ # Handle edit mode
966
+ if editor_state["edit_mode"]:
967
+ original_name = editor_state["original_name"]
968
+ if original_name != name:
969
+ del st.session_state.project_data["materials"]["project"][original_name]
970
+ st.session_state.project_data["materials"]["project"][name] = material_data
971
+ st.success(f"Material '{name}' updated successfully.")
972
+ logger.info(f"Updated material '{name}' in project")
973
  else:
974
+ st.session_state.project_data["materials"]["project"][name] = material_data
975
+ st.success(f"Material '{name}' added to your project.")
976
+ logger.info(f"Added new material '{name}' to project")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
977
 
978
+ # Reset editor and trigger rerun
979
+ reset_material_editor()
980
+ logger.info("Triggering rerun after saving material editor")
981
+ st.rerun()
982
+
983
+ # Handle clear button
984
+ if clear_button:
985
+ reset_material_editor()
986
+ logger.info("Triggering rerun after clearing material editor")
987
+ st.rerun()
988
 
989
  def display_fenestration_editor():
990
  """Display the fenestration editor form."""
991
+ with st.form("fenestration_editor_form"):
992
+ editor_state = st.session_state.fenestration_editor
993
+ is_library = editor_state.get("is_library", False)
994
+
995
+ # Fenestration name
996
+ name = st.text_input(
997
+ "Fenestration Name",
998
+ value=editor_state["name"],
999
+ help="Enter a unique name for the fenestration.",
1000
+ disabled=is_library
1001
+ )
1002
+
1003
+ # Create two columns for layout
1004
+ col1, col2 = st.columns(2)
1005
+
1006
+ with col1:
1007
+ # Type
1008
+ fenestration_type = st.selectbox(
1009
+ "Type",
1010
+ FENESTRATION_TYPES,
1011
+ index=FENESTRATION_TYPES.index(editor_state["type"]) if editor_state["type"] in FENESTRATION_TYPES else 0,
1012
+ help="Select the fenestration type.",
1013
  disabled=is_library
1014
  )
1015
 
1016
+ # U-value
1017
+ u_value = st.number_input(
1018
+ "U-Value (W/m²·K)",
1019
+ min_value=0.1,
1020
+ max_value=10.0,
1021
+ value=float(editor_state["u_value"]),
1022
+ format="%.2f",
1023
+ help="U-value in W/m²·K. Lower values indicate better insulation.",
1024
+ disabled=is_library
1025
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1026
 
1027
+ # SHGC
1028
+ shgc = st.number_input(
1029
+ "Solar Heat Gain Coefficient (SHGC)",
1030
+ min_value=0.0,
1031
+ max_value=1.0,
1032
+ value=float(editor_state["shgc"]),
1033
+ format="%.2f",
1034
+ help="Solar Heat Gain Coefficient (0-1). Lower values indicate less solar heat transmission.",
1035
+ disabled=is_library
1036
+ )
1037
+
1038
+ with col2:
1039
+ # Visible transmittance
1040
+ visible_transmittance = st.number_input(
1041
+ "Visible Transmittance",
1042
+ min_value=0.0,
1043
+ max_value=1.0,
1044
+ value=float(editor_state["visible_transmittance"]),
1045
+ format="%.2f",
1046
+ help="Visible Transmittance (0-1). Higher values indicate more visible light transmission.",
1047
+ disabled=is_library
1048
+ )
1049
 
1050
+ # Thickness
1051
+ thickness = st.number_input(
1052
+ "Thickness (m)",
1053
+ min_value=0.001,
1054
+ max_value=0.1,
1055
+ value=float(editor_state["thickness"]),
1056
+ format="%.3f",
1057
+ help="Thickness in meters.",
1058
+ disabled=is_library
1059
+ )
1060
+
1061
+ # Additional properties
1062
+ st.subheader("Additional Properties")
1063
+ col1, col2 = st.columns(2)
1064
+
1065
+ with col1:
1066
+ # Embodied carbon
1067
+ embodied_carbon = st.number_input(
1068
+ "Embodied Carbon (kg CO₂e/m²)",
1069
+ min_value=0.0,
1070
+ max_value=5000.0,
1071
+ value=float(editor_state["embodied_carbon"]),
1072
+ format="%.1f",
1073
+ help="Embodied carbon in kg CO₂e per square meter.",
1074
+ disabled=is_library
1075
+ )
1076
+
1077
+ with col2:
1078
+ # Cost
1079
+ cost = st.number_input(
1080
+ "Cost (USD/m²)",
1081
+ min_value=0.0,
1082
+ max_value=5000.0,
1083
+ value=float(editor_state["cost"]),
1084
+ format="%.1f",
1085
+ help="Fenestration cost in USD per square meter.",
1086
+ disabled=is_library
1087
+ )
1088
+
1089
+ # Form submission buttons
1090
+ col1, col2 = st.columns(2)
1091
+
1092
+ with col1:
1093
+ submit_button = st.form_submit_button("Save Fenestration", disabled=is_library)
1094
+
1095
+ with col2:
1096
+ clear_button = st.form_submit_button("Clear Form")
1097
+
1098
+ # Handle form submission
1099
+ if submit_button and not is_library:
1100
+ # Validate inputs
1101
+ validation_errors = validate_fenestration(
1102
+ name, fenestration_type, u_value, shgc, visible_transmittance, thickness,
1103
+ embodied_carbon, cost, editor_state["edit_mode"], editor_state["original_name"]
1104
+ )
1105
 
1106
+ if validation_errors:
1107
+ for error in validation_errors:
1108
+ st.error(error)
1109
+ else:
1110
+ # Create fenestration data
1111
+ fenestration_data = {
1112
+ "type": fenestration_type,
1113
+ "u_value": u_value,
1114
+ "shgc": shgc,
1115
+ "visible_transmittance": visible_transmittance,
1116
+ "thickness": thickness,
1117
+ "embodied_carbon": embodied_carbon,
1118
+ "cost": cost
1119
+ }
1120
 
1121
+ # Handle edit mode
1122
+ if editor_state["edit_mode"]:
1123
+ original_name = editor_state["original_name"]
1124
+ if original_name != name:
1125
+ del st.session_state.project_data["fenestrations"]["project"][original_name]
1126
+ st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
1127
+ st.success(f"Fenestration '{name}' updated successfully.")
1128
+ logger.info(f"Updated fenestration '{name}' in project")
1129
  else:
1130
+ st.session_state.project_data["fenestrations"]["project"][name] = fenestration_data
1131
+ st.success(f"Fenestration '{name}' added to your project.")
1132
+ logger.info(f"Added new fenestration '{name}' to project")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1133
 
1134
+ # Reset editor and trigger rerun
1135
+ reset_fenestration_editor()
1136
+ logger.info("Triggering rerun after saving fenestration editor")
1137
+ st.rerun()
1138
+
1139
+ # Handle clear button
1140
+ if clear_button:
1141
+ reset_fenestration_editor()
1142
+ logger.info("Triggering rerun after clearing fenestration editor")
1143
+ st.rerun()
1144
 
1145
  def validate_material(
1146
  name: str, category: str, thermal_conductivity: float, density: float,