mabuseif commited on
Commit
9807c3b
verified
1 Parent(s): 9a38ff5

Update app/internal_loads.py

Browse files
Files changed (1) hide show
  1. app/internal_loads.py +378 -493
app/internal_loads.py CHANGED
@@ -3,7 +3,7 @@ BuildSustain - Internal Loads Module
3
 
4
  This module handles the internal loads functionality of the BuildSustain application,
5
  allowing users to define occupancy, lighting, equipment, ventilation, infiltration, and schedules.
6
- It provides comprehensive load management with the new UI structure matching other modules.
7
 
8
  Developed by: Dr Majed Abuseif, Deakin University
9
  漏 2025
@@ -49,8 +49,11 @@ def display_internal_loads_page():
49
  with st.expander("Help & Information"):
50
  display_internal_loads_help()
51
 
 
 
 
 
52
  # Check if rerun is pending
53
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
54
  if st.session_state.module_rerun_flags.get("internal_loads", False):
55
  st.session_state.module_rerun_flags["internal_loads"] = False
56
  st.rerun()
@@ -87,7 +90,6 @@ def display_internal_loads_page():
87
  def initialize_internal_loads():
88
  """
89
  Initialize internal loads in session state if not present.
90
- [FIX]: Removed redundant initialization as main.py handles this.
91
  Logging a warning if initialization is needed for debugging.
92
  """
93
  if "internal_loads" not in st.session_state.project_data:
@@ -101,16 +103,15 @@ def initialize_internal_loads():
101
  }
102
 
103
  def display_people_tab():
104
- """Display the people tab content with two-column layout."""
 
 
 
105
  # Split the display into two columns
106
  col1, col2 = st.columns([3, 2])
107
 
108
  with col1:
109
  st.subheader("Saved People Groups")
110
-
111
- # Get people from session state
112
- people_groups = st.session_state.project_data["internal_loads"]["people"]
113
-
114
  if people_groups:
115
  display_people_table(people_groups)
116
  else:
@@ -119,11 +120,13 @@ def display_people_tab():
119
  with col2:
120
  st.subheader("People Group Editor/Creator")
121
 
122
- # Check if we have an editor state for people
123
  if "people_editor" not in st.session_state:
124
  st.session_state.people_editor = {}
 
 
125
 
126
- # Display the people editor form
127
  with st.form("people_editor_form", clear_on_submit=True):
128
  editor_state = st.session_state.get("people_editor", {})
129
  is_edit = editor_state.get("is_edit", False)
@@ -195,79 +198,74 @@ def display_people_tab():
195
  # Submit buttons
196
  col1, col2 = st.columns(2)
197
  with col1:
198
- submit_label = "Update People Group" if is_edit else "Add People Group"
199
- submit = st.form_submit_button(submit_label)
200
 
201
  with col2:
202
- cancel = st.form_submit_button("Cancel")
203
 
204
  # Handle form submission
205
  if submit:
206
  # Validate inputs
207
  if not name.strip():
208
  st.error("Group name is required.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  else:
210
- # Get activity data
211
- activity_data = PEOPLE_ACTIVITY_LEVELS[activity_level]
212
-
213
- # Create people group data
214
- people_data = {
215
- "id": str(uuid.uuid4()),
216
- "name": name.strip(),
217
- "num_people": num_people,
218
- "activity_level": activity_level,
219
- "activity_data": activity_data,
220
- "clo_summer": clo_summer,
221
- "clo_winter": clo_winter,
222
- "schedule": schedule,
223
- "zone": zone,
224
- "sensible_heat_per_person": (activity_data["sensible_min_w"] + activity_data["sensible_max_w"]) / 2,
225
- "latent_heat_per_person": (activity_data["latent_min_w"] + activity_data["latent_max_w"]) / 2,
226
- "total_sensible_heat": num_people * (activity_data["sensible_min_w"] + activity_data["sensible_max_w"]) / 2,
227
- "total_latent_heat": num_people * (activity_data["latent_min_w"] + activity_data["latent_max_w"]) / 2
228
- }
229
-
230
- # Check if editing or adding new
231
- if is_edit and st.session_state.people_editor.get("edit_id"):
232
- # Update existing people group
233
- edit_id = st.session_state.people_editor["edit_id"]
234
- people_groups = st.session_state.project_data["internal_loads"]["people"]
235
-
236
- for i, group in enumerate(people_groups):
237
- if group.get("id") == edit_id:
238
- people_groups[i] = people_data
239
- break
240
-
241
- st.success(f"People group '{name}' updated successfully!")
242
- else:
243
- # Add new people group
244
- st.session_state.project_data["internal_loads"]["people"].append(people_data)
245
- st.success(f"People group '{name}' added successfully!")
246
-
247
- # Clear editor state
248
- st.session_state.people_editor = {}
249
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
250
- st.session_state.module_rerun_flags["internal_loads"] = True
251
- st.rerun()
252
 
253
- if cancel:
254
  # Clear editor state
255
  st.session_state.people_editor = {}
256
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
257
  st.session_state.module_rerun_flags["internal_loads"] = True
258
- st.rerun()
259
 
260
  def display_lighting_tab():
261
- """Display the lighting tab content with two-column layout."""
 
 
 
262
  # Split the display into two columns
263
  col1, col2 = st.columns([3, 2])
264
 
265
  with col1:
266
  st.subheader("Saved Lighting Systems")
267
-
268
- # Get lighting from session state
269
- lighting_systems = st.session_state.project_data["internal_loads"]["lighting"]
270
-
271
  if lighting_systems:
272
  display_lighting_table(lighting_systems)
273
  else:
@@ -276,15 +274,17 @@ def display_lighting_tab():
276
  with col2:
277
  st.subheader("Lighting System Editor/Creator")
278
 
279
- # Check if we have an editor state for lighting
280
  if "lighting_editor" not in st.session_state:
281
  st.session_state.lighting_editor = {}
 
 
282
 
283
  # Get building type for default values
284
  building_type = st.session_state.project_data["building_info"].get("building_type")
285
  default_lighting_density = DEFAULT_BUILDING_INTERNALS.get(building_type, DEFAULT_BUILDING_INTERNALS["Other"])["lighting_density"]
286
 
287
- # Display the lighting editor form
288
  with st.form("lighting_editor_form", clear_on_submit=True):
289
  editor_state = st.session_state.get("lighting_editor", {})
290
  is_edit = editor_state.get("is_edit", False)
@@ -297,7 +297,6 @@ def display_lighting_tab():
297
  )
298
 
299
  # Area
300
- # [FIX]: Corrected session state reference from st.session_state.building_info to st.session_state.project_data["building_info"]
301
  area = st.number_input(
302
  "Area (m虏)",
303
  min_value=1.0,
@@ -341,10 +340,6 @@ def display_lighting_tab():
341
  help="Fraction of heat released as convection."
342
  )
343
 
344
- # Validate fractions
345
- if abs(radiative_fraction + convective_fraction - 1.0) > 0.01:
346
- st.warning("Radiative and convective fractions should sum to 1.0")
347
-
348
  # Schedule selection
349
  available_schedules = list(st.session_state.project_data["internal_loads"]["schedules"].keys())
350
  schedule = st.selectbox(
@@ -364,74 +359,70 @@ def display_lighting_tab():
364
  # Submit buttons
365
  col1, col2 = st.columns(2)
366
  with col1:
367
- submit_label = "Update Lighting System" if is_edit else "Add Lighting System"
368
- submit = st.form_submit_button(submit_label)
369
 
370
  with col2:
371
- cancel = st.form_submit_button("Cancel")
372
 
373
  # Handle form submission
374
  if submit:
375
  # Validate inputs
376
  if not name.strip():
377
  st.error("System name is required.")
378
- elif abs(radiative_fraction + convective_fraction - 1.0) > 0.01:
 
379
  st.error("Radiative and convective fractions must sum to 1.0")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  else:
381
- # Create lighting system data
382
- lighting_data = {
383
- "id": str(uuid.uuid4()),
384
- "name": name.strip(),
385
- "area": area,
386
- "lpd": lpd,
387
- "total_power": area * lpd,
388
- "radiative_fraction": radiative_fraction,
389
- "convective_fraction": convective_fraction,
390
- "schedule": schedule,
391
- "zone": zone
392
- }
393
-
394
- # Check if editing or adding new
395
- if is_edit and st.session_state.lighting_editor.get("edit_id"):
396
- # Update existing lighting system
397
- edit_id = st.session_state.lighting_editor["edit_id"]
398
- lighting_systems = st.session_state.project_data["internal_loads"]["lighting"]
399
-
400
- for i, system in enumerate(lighting_systems):
401
- if system.get("id") == edit_id:
402
- lighting_systems[i] = lighting_data
403
- break
404
-
405
- st.success(f"Lighting system '{name}' updated successfully!")
406
- else:
407
- # Add new lighting system
408
- st.session_state.project_data["internal_loads"]["lighting"].append(lighting_data)
409
- st.success(f"Lighting system '{name}' added successfully!")
410
-
411
- # Clear editor state
412
- st.session_state.lighting_editor = {}
413
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
414
- st.session_state.module_rerun_flags["internal_loads"] = True
415
- st.rerun()
416
 
417
- if cancel:
418
  # Clear editor state
419
  st.session_state.lighting_editor = {}
420
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
421
  st.session_state.module_rerun_flags["internal_loads"] = True
422
- st.rerun()
423
 
424
  def display_equipment_tab():
425
- """Display the equipment tab content with two-column layout."""
 
 
 
426
  # Split the display into two columns
427
  col1, col2 = st.columns([3, 2])
428
 
429
  with col1:
430
  st.subheader("Saved Equipment")
431
-
432
- # Get equipment from session state
433
- equipment_systems = st.session_state.project_data["internal_loads"]["equipment"]
434
-
435
  if equipment_systems:
436
  display_equipment_table(equipment_systems)
437
  else:
@@ -440,15 +431,17 @@ def display_equipment_tab():
440
  with col2:
441
  st.subheader("Equipment Editor/Creator")
442
 
443
- # Check if we have an editor state for equipment
444
  if "equipment_editor" not in st.session_state:
445
  st.session_state.equipment_editor = {}
 
 
446
 
447
  # Get building type for default values
448
  building_type = st.session_state.project_data["building_info"].get("building_type")
449
  default_equipment_data = DEFAULT_BUILDING_INTERNALS.get(building_type, DEFAULT_BUILDING_INTERNALS["Other"])["equipment_heat_gains"]
450
 
451
- # Display the equipment editor form
452
  with st.form("equipment_editor_form", clear_on_submit=True):
453
  editor_state = st.session_state.get("equipment_editor", {})
454
  is_edit = editor_state.get("is_edit", False)
@@ -461,7 +454,6 @@ def display_equipment_tab():
461
  )
462
 
463
  # Area
464
- # [FIX]: Corrected session state reference from st.session_state.building_info to st.session_state.project_data["building_info"]
465
  area = st.number_input(
466
  "Area (m虏)",
467
  min_value=1.0,
@@ -519,10 +511,6 @@ def display_equipment_tab():
519
  help="Fraction of sensible heat released as convection."
520
  )
521
 
522
- # Validate fractions
523
- if abs(radiative_fraction + convective_fraction - 1.0) > 0.01:
524
- st.warning("Radiative and convective fractions should sum to 1.0")
525
-
526
  # Schedule selection
527
  available_schedules = list(st.session_state.project_data["internal_loads"]["schedules"].keys())
528
  schedule = st.selectbox(
@@ -542,76 +530,72 @@ def display_equipment_tab():
542
  # Submit buttons
543
  col1, col2 = st.columns(2)
544
  with col1:
545
- submit_label = "Update Equipment" if is_edit else "Add Equipment"
546
- submit = st.form_submit_button(submit_label)
547
 
548
  with col2:
549
- cancel = st.form_submit_button("Cancel")
550
 
551
  # Handle form submission
552
  if submit:
553
  # Validate inputs
554
  if not name.strip():
555
  st.error("Equipment name is required.")
556
- elif abs(radiative_fraction + convective_fraction - 1.0) > 0.01:
 
557
  st.error("Radiative and convective fractions must sum to 1.0")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  else:
559
- # Create equipment data
560
- equipment_data = {
561
- "id": str(uuid.uuid4()),
562
- "name": name.strip(),
563
- "area": area,
564
- "sensible_gain": sensible_gain,
565
- "latent_gain": latent_gain,
566
- "total_sensible_power": area * sensible_gain,
567
- "total_latent_power": area * latent_gain,
568
- "radiative_fraction": radiative_fraction,
569
- "convective_fraction": convective_fraction,
570
- "schedule": schedule,
571
- "zone": zone
572
- }
573
-
574
- # Check if editing or adding new
575
- if is_edit and st.session_state.equipment_editor.get("edit_id"):
576
- # Update existing equipment
577
- edit_id = st.session_state.equipment_editor["edit_id"]
578
- equipment_systems = st.session_state.project_data["internal_loads"]["equipment"]
579
-
580
- for i, system in enumerate(equipment_systems):
581
- if system.get("id") == edit_id:
582
- equipment_systems[i] = equipment_data
583
- break
584
-
585
- st.success(f"Equipment '{name}' updated successfully!")
586
- else:
587
- # Add new equipment
588
- st.session_state.project_data["internal_loads"]["equipment"].append(equipment_data)
589
- st.success(f"Equipment '{name}' added successfully!")
590
-
591
- # Clear editor state
592
- st.session_state.equipment_editor = {}
593
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
594
- st.session_state.module_rerun_flags["internal_loads"] = True
595
- st.rerun()
596
 
597
- if cancel:
598
  # Clear editor state
599
  st.session_state.equipment_editor = {}
600
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
601
  st.session_state.module_rerun_flags["internal_loads"] = True
602
- st.rerun()
603
 
604
  def display_ventilation_infiltration_tab():
605
- """Display the ventilation and infiltration tab content with two-column layout."""
 
 
 
606
  # Split the display into two columns
607
  col1, col2 = st.columns([3, 2])
608
 
609
  with col1:
610
  st.subheader("Saved Ventilation & Infiltration")
611
-
612
- # Get ventilation/infiltration from session state
613
- vent_inf_systems = st.session_state.project_data["internal_loads"]["ventilation_infiltration"]
614
-
615
  if vent_inf_systems:
616
  display_ventilation_infiltration_table(vent_inf_systems)
617
  else:
@@ -620,15 +604,17 @@ def display_ventilation_infiltration_tab():
620
  with col2:
621
  st.subheader("Ventilation/Infiltration Editor/Creator")
622
 
623
- # Check if we have an editor state for ventilation/infiltration
624
  if "vent_inf_editor" not in st.session_state:
625
  st.session_state.vent_inf_editor = {}
 
 
626
 
627
  # Get building type for default values
628
  building_type = st.session_state.project_data["building_info"].get("building_type")
629
  default_building_data = DEFAULT_BUILDING_INTERNALS.get(building_type, DEFAULT_BUILDING_INTERNALS["Other"])
630
 
631
- # Display the ventilation/infiltration editor form
632
  with st.form("vent_inf_editor_form", clear_on_submit=True):
633
  editor_state = st.session_state.get("vent_inf_editor", {})
634
  is_edit = editor_state.get("is_edit", False)
@@ -649,7 +635,6 @@ def display_ventilation_infiltration_tab():
649
  )
650
 
651
  # Area
652
- # [FIX]: Corrected session state reference from st.session_state.building_info to st.session_state.project_data["building_info"]
653
  area = st.number_input(
654
  "Area (m虏)",
655
  min_value=1.0,
@@ -669,8 +654,7 @@ def display_ventilation_infiltration_tab():
669
  format="%.2f",
670
  help="Ventilation rate in liters per second per square meter."
671
  )
672
-
673
- air_change_rate = 0.0 # Not used for ventilation
674
  else:
675
  # Air change rate for infiltration
676
  air_change_rate = st.number_input(
@@ -681,8 +665,7 @@ def display_ventilation_infiltration_tab():
681
  format="%.2f",
682
  help="Air change rate in air changes per hour."
683
  )
684
-
685
- ventilation_rate = 0.0 # Not used for infiltration
686
 
687
  # Schedule selection
688
  available_schedules = list(st.session_state.project_data["internal_loads"]["schedules"].keys())
@@ -703,59 +686,55 @@ def display_ventilation_infiltration_tab():
703
  # Submit buttons
704
  col1, col2 = st.columns(2)
705
  with col1:
706
- submit_label = "Update System" if is_edit else "Add System"
707
- submit = st.form_submit_button(submit_label)
708
 
709
  with col2:
710
- cancel = st.form_submit_button("Cancel")
711
 
712
  # Handle form submission
713
  if submit:
714
  # Validate inputs
715
  if not name.strip():
716
  st.error("System name is required.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
717
  else:
718
- # Create ventilation/infiltration data
719
- vent_inf_data = {
720
- "id": str(uuid.uuid4()),
721
- "name": name.strip(),
722
- "system_type": system_type,
723
- "area": area,
724
- "ventilation_rate": ventilation_rate,
725
- "air_change_rate": air_change_rate,
726
- "schedule": schedule,
727
- "zone": zone
728
- }
729
-
730
- # Check if editing or adding new
731
- if is_edit and st.session_state.vent_inf_editor.get("edit_id"):
732
- # Update existing system
733
- edit_id = st.session_state.vent_inf_editor["edit_id"]
734
- vent_inf_systems = st.session_state.project_data["internal_loads"]["ventilation_infiltration"]
735
-
736
- for i, system in enumerate(vent_inf_systems):
737
- if system.get("id") == edit_id:
738
- vent_inf_systems[i] = vent_inf_data
739
- break
740
-
741
- st.success(f"{system_type} system '{name}' updated successfully!")
742
- else:
743
- # Add new system
744
- st.session_state.project_data["internal_loads"]["ventilation_infiltration"].append(vent_inf_data)
745
- st.success(f"{system_type} system '{name}' added successfully!")
746
-
747
- # Clear editor state
748
- st.session_state.vent_inf_editor = {}
749
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
750
- st.session_state.module_rerun_flags["internal_loads"] = True
751
- st.rerun()
752
 
753
- if cancel:
754
  # Clear editor state
755
  st.session_state.vent_inf_editor = {}
756
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
757
  st.session_state.module_rerun_flags["internal_loads"] = True
758
- st.rerun()
759
 
760
  def display_schedules_tab():
761
  import streamlit as st
@@ -782,7 +761,7 @@ def display_schedules_tab():
782
  editor_state = st.session_state.schedule_editor
783
  schedules = st.session_state.project_data["internal_loads"]["schedules"]
784
 
785
- # ---------------------- Handle template change ----------------------
786
  template_options = list(DEFAULT_SCHEDULE_TEMPLATES.keys())
787
  selected_template = st.selectbox(
788
  "Select Template",
@@ -799,7 +778,6 @@ def display_schedules_tab():
799
  tpl = DEFAULT_SCHEDULE_TEMPLATES[selected_template]
800
  st.session_state.schedule_editor["weekday"] = tpl["weekday"]
801
  st.session_state.schedule_editor["weekend"] = tpl["weekend"]
802
- # Store slider values in session state for preloading
803
  for hour in range(24):
804
  st.session_state[f"weekday_slider_{hour}_value"] = tpl["weekday"][hour]
805
  st.session_state[f"weekend_slider_{hour}_value"] = tpl["weekend"][hour]
@@ -810,15 +788,13 @@ def display_schedules_tab():
810
  st.session_state.module_rerun_flags["internal_loads"] = True
811
  st.rerun()
812
 
813
- # ---------------------- UI FORM for name/description and actions ----------------------
814
  with st.form("schedule_form"):
815
  name = st.text_input("Schedule Name", value=editor_state.get("name", ""))
816
  description = st.text_area("Description", value=editor_state.get("description", ""))
817
 
818
- # ---------------------- SLIDERS LAYOUT ----------------------
819
  st.markdown("### Schedule Sliders")
820
-
821
- # Table headers
822
  col_hour, col_wd, col_we = st.columns([0.4, 2.0, 2.0])
823
  with col_hour:
824
  st.markdown("<div style='text-align:center; font-weight:bold; font-size:18px;'>Hour</div>", unsafe_allow_html=True)
@@ -842,10 +818,8 @@ def display_schedules_tab():
842
 
843
  for hour in range(24):
844
  col_hour, col_wd, col_we = st.columns([0.4, 2.0, 2.0])
845
-
846
  with col_hour:
847
  st.markdown(f"<div style='text-align:center; font-size:12px'>{hour:02d}</div>", unsafe_allow_html=True)
848
-
849
  with col_wd:
850
  val = st.slider(
851
  label=f"Weekday {hour:02d}",
@@ -858,7 +832,6 @@ def display_schedules_tab():
858
  format=None
859
  )
860
  weekday_values.append(val)
861
-
862
  with col_we:
863
  val = st.slider(
864
  label=f"Weekend {hour:02d}",
@@ -872,7 +845,7 @@ def display_schedules_tab():
872
  )
873
  weekend_values.append(val)
874
 
875
- # ---------------------- Action Buttons ----------------------
876
  col1, col2, col3 = st.columns(3)
877
  with col1:
878
  submit_label = "Update Schedule" if editor_state.get("is_edit") else "Save Schedule"
@@ -882,7 +855,7 @@ def display_schedules_tab():
882
  with col3:
883
  delete_submitted = st.form_submit_button("Delete Selected Schedule")
884
 
885
- # ---------------------- Saved Schedules Selector ----------------------
886
  st.markdown("### Saved Schedules")
887
  saved_schedule = st.selectbox(
888
  "Select Saved Schedule",
@@ -891,7 +864,7 @@ def display_schedules_tab():
891
  help="Select a saved schedule to edit or delete."
892
  )
893
 
894
- # ---------------------- Save logic ----------------------
895
  if submitted:
896
  action_id = str(uuid.uuid4())
897
  if st.session_state.schedule_action.get("id") != action_id:
@@ -909,7 +882,6 @@ def display_schedules_tab():
909
  if editor_state.get("is_edit") and editor_state.get("original_name") and editor_state.get("original_name") != name:
910
  if editor_state["original_name"] in schedules:
911
  del schedules[editor_state["original_name"]]
912
- # Reset editor state and slider values
913
  st.session_state.schedule_editor = DEFAULT_STATE.copy()
914
  for hour in range(24):
915
  st.session_state[f"weekday_slider_{hour}_value"] = 0.0
@@ -919,7 +891,7 @@ def display_schedules_tab():
919
  st.session_state.module_rerun_flags["internal_loads"] = True
920
  st.rerun()
921
 
922
- # ---------------------- Edit logic ----------------------
923
  if edit_submitted and saved_schedule:
924
  action_id = str(uuid.uuid4())
925
  if st.session_state.schedule_action.get("id") != action_id:
@@ -928,7 +900,6 @@ def display_schedules_tab():
928
  st.error("Default schedules cannot be edited.")
929
  else:
930
  schedule_data = schedules[saved_schedule]
931
- # Update editor state
932
  st.session_state.schedule_editor = {
933
  "is_edit": True,
934
  "original_name": saved_schedule,
@@ -938,7 +909,6 @@ def display_schedules_tab():
938
  "weekend": schedule_data.get("weekend", [0.0] * 24),
939
  "template": "None"
940
  }
941
- # Preload slider values
942
  for hour in range(24):
943
  st.session_state[f"weekday_slider_{hour}_value"] = schedule_data.get("weekday", [0.0] * 24)[hour]
944
  st.session_state[f"weekend_slider_{hour}_value"] = schedule_data.get("weekend", [0.0] * 24)[hour]
@@ -946,7 +916,7 @@ def display_schedules_tab():
946
  st.session_state.module_rerun_flags["internal_loads"] = True
947
  st.rerun()
948
 
949
- # ---------------------- Delete logic ----------------------
950
  if delete_submitted and saved_schedule:
951
  action_id = str(uuid.uuid4())
952
  if st.session_state.schedule_action.get("id") != action_id:
@@ -957,7 +927,6 @@ def display_schedules_tab():
957
  st.error(f"Schedule '{saved_schedule}' is in use and cannot be deleted.")
958
  else:
959
  del schedules[saved_schedule]
960
- # Reset editor state and slider values
961
  st.session_state.schedule_editor = DEFAULT_STATE.copy()
962
  for hour in range(24):
963
  st.session_state[f"weekday_slider_{hour}_value"] = 0.0
@@ -970,7 +939,6 @@ def display_schedules_tab():
970
  def is_schedule_in_use(schedule_name: str) -> bool:
971
  """
972
  Check if a schedule is in use by any people, lighting, equipment, or ventilation/infiltration systems.
973
- [FIX]: Added to prevent deletion of schedules that are referenced elsewhere.
974
 
975
  Args:
976
  schedule_name: Name of the schedule to check.
@@ -995,274 +963,197 @@ def is_schedule_in_use(schedule_name: str) -> bool:
995
 
996
  def display_people_table(people_groups: List[Dict[str, Any]]):
997
  """Display people groups in a table format with edit/delete buttons."""
998
- if not people_groups:
999
- return
1000
-
1001
- # Create table data
1002
- table_data = []
1003
- for group in people_groups:
1004
- table_data.append({
1005
- "Name": group.get("name", "Unknown"),
1006
- "Number": group.get("num_people", 0),
1007
- "Activity": group.get("activity_level", "Unknown"),
1008
- "Sensible (W)": f"{group.get('total_sensible_heat', 0):.1f}",
1009
- "Latent (W)": f"{group.get('total_latent_heat', 0):.1f}",
1010
- "Zone": group.get("zone", "Unknown"),
1011
- "Schedule": group.get("schedule", "Unknown"),
1012
- "Actions": group.get("id", "")
1013
- })
1014
 
1015
- if table_data:
1016
- df = pd.DataFrame(table_data)
 
 
 
 
 
 
1017
 
1018
- # Display table
1019
- st.dataframe(df.drop('Actions', axis=1), use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1020
 
1021
- # Display action buttons
1022
- st.write("**Actions:**")
1023
- for i, row in enumerate(table_data):
1024
- group_id = row["Actions"]
1025
- group_name = row["Name"]
1026
-
1027
- col1, col2, col3 = st.columns([2, 1, 1])
1028
-
1029
- with col1:
1030
- st.write(f"{i+1}. {group_name}")
1031
-
1032
- with col2:
1033
- if st.button("Edit", key=f"edit_people_{group_id}_{i}"):
1034
- # Set up editor for editing
1035
- group = next((g for g in people_groups if g.get("id") == group_id), None)
1036
- if group:
1037
- st.session_state.people_editor = {
1038
- "is_edit": True,
1039
- "edit_id": group_id,
1040
- "name": group.get("name", ""),
1041
- "num_people": group.get("num_people", 10),
1042
- "activity_level": group.get("activity_level", list(PEOPLE_ACTIVITY_LEVELS.keys())[0]),
1043
- "clo_summer": group.get("clo_summer", 0.5),
1044
- "clo_winter": group.get("clo_winter", 1.0),
1045
- "schedule": group.get("schedule", "Continuous"),
1046
- "zone": group.get("zone", "Whole Building")
1047
- }
1048
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1049
- st.session_state.module_rerun_flags["internal_loads"] = True
1050
- st.rerun()
1051
-
1052
- with col3:
1053
- if st.button("Delete", key=f"delete_people_{group_id}_{i}"):
1054
- # Delete people group
1055
- st.session_state.project_data["internal_loads"]["people"] = [
1056
- g for g in people_groups if g.get("id") != group_id
1057
- ]
1058
- st.success(f"People group '{group_name}' deleted!")
1059
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1060
- st.session_state.module_rerun_flags["internal_loads"] = True
1061
- st.rerun()
1062
 
1063
  def display_lighting_table(lighting_systems: List[Dict[str, Any]]):
1064
  """Display lighting systems in a table format with edit/delete buttons."""
1065
- if not lighting_systems:
1066
- return
 
 
 
 
 
 
 
1067
 
1068
- # Create table data
1069
- table_data = []
1070
- for system in lighting_systems:
1071
- table_data.append({
1072
- "Name": system.get("name", "Unknown"),
1073
- "Area (m虏)": f"{system.get('area', 0):.1f}",
1074
- "LPD (W/m虏)": f"{system.get('lpd', 0):.2f}",
1075
- "Total Power (W)": f"{system.get('total_power', 0):.1f}",
1076
- "Zone": system.get("zone", "Unknown"),
1077
- "Schedule": system.get("schedule", "Unknown"),
1078
- "Actions": system.get("id", "")
1079
- })
1080
-
1081
- if table_data:
1082
- df = pd.DataFrame(table_data)
1083
 
1084
- # Display table
1085
- st.dataframe(df.drop('Actions', axis=1), use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1086
 
1087
- # Display action buttons
1088
- st.write("**Actions:**")
1089
- for i, row in enumerate(table_data):
1090
- system_id = row["Actions"]
1091
- system_name = row["Name"]
1092
-
1093
- col1, col2, col3 = st.columns([2, 1, 1])
1094
-
1095
- with col1:
1096
- st.write(f"{i+1}. {system_name}")
1097
-
1098
- with col2:
1099
- if st.button("Edit", key=f"edit_lighting_{system_id}_{i}"):
1100
- # Set up editor for editing
1101
- system = next((s for s in lighting_systems if s.get("id") == system_id), None)
1102
- if system:
1103
- st.session_state.lighting_editor = {
1104
- "is_edit": True,
1105
- "edit_id": system_id,
1106
- "name": system.get("name", ""),
1107
- "area": system.get("area", 100.0),
1108
- "lpd": system.get("lpd", 12.0),
1109
- "radiative_fraction": system.get("radiative_fraction", 0.4),
1110
- "convective_fraction": system.get("convective_fraction", 0.6),
1111
- "schedule": system.get("schedule", "Continuous"),
1112
- "zone": system.get("zone", "Whole Building")
1113
- }
1114
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1115
- st.session_state.module_rerun_flags["internal_loads"] = True
1116
- st.rerun()
1117
-
1118
- with col3:
1119
- if st.button("Delete", key=f"delete_lighting_{system_id}_{i}"):
1120
- # Delete lighting system
1121
- st.session_state.project_data["internal_loads"]["lighting"] = [
1122
- s for s in lighting_systems if s.get("id") != system_id
1123
- ]
1124
- st.success(f"Lighting system '{system_name}' deleted!")
1125
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1126
- st.session_state.module_rerun_flags["internal_loads"] = True
1127
- st.rerun()
1128
 
1129
  def display_equipment_table(equipment_systems: List[Dict[str, Any]]):
1130
  """Display equipment systems in a table format with edit/delete buttons."""
1131
- if not equipment_systems:
1132
- return
 
 
 
 
 
 
 
1133
 
1134
- # Create table data
1135
- table_data = []
1136
- for system in equipment_systems:
1137
- table_data.append({
1138
- "Name": system.get("name", "Unknown"),
1139
- "Area (m虏)": f"{system.get('area', 0):.1f}",
1140
- "Sensible (W)": f"{system.get('total_sensible_power', 0):.1f}",
1141
- "Latent (W)": f"{system.get('total_latent_power', 0):.1f}",
1142
- "Zone": system.get("zone", "Unknown"),
1143
- "Schedule": system.get("schedule", "Unknown"),
1144
- "Actions": system.get("id", "")
1145
- })
1146
-
1147
- if table_data:
1148
- df = pd.DataFrame(table_data)
1149
 
1150
- # Display table
1151
- st.dataframe(df.drop('Actions', axis=1), use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1152
 
1153
- # Display action buttons
1154
- st.write("**Actions:**")
1155
- for i, row in enumerate(table_data):
1156
- system_id = row["Actions"]
1157
- system_name = row["Name"]
1158
-
1159
- col1, col2, col3 = st.columns([2, 1, 1])
1160
-
1161
- with col1:
1162
- st.write(f"{i+1}. {system_name}")
1163
-
1164
- with col2:
1165
- if st.button("Edit", key=f"edit_equipment_{system_id}_{i}"):
1166
- # Set up editor for editing
1167
- system = next((s for s in equipment_systems if s.get("id") == system_id), None)
1168
- if system:
1169
- st.session_state.equipment_editor = {
1170
- "is_edit": True,
1171
- "edit_id": system_id,
1172
- "name": system.get("name", ""),
1173
- "area": system.get("area", 100.0),
1174
- "sensible_gain": system.get("sensible_gain", 10.0),
1175
- "latent_gain": system.get("latent_gain", 2.0),
1176
- "radiative_fraction": system.get("radiative_fraction", 0.5),
1177
- "convective_fraction": system.get("convective_fraction", 0.5),
1178
- "schedule": system.get("schedule", "Continuous"),
1179
- "zone": system.get("zone", "Whole Building")
1180
- }
1181
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1182
- st.session_state.module_rerun_flags["internal_loads"] = True
1183
- st.rerun()
1184
-
1185
- with col3:
1186
- if st.button("Delete", key=f"delete_equipment_{system_id}_{i}"):
1187
- # Delete equipment system
1188
- st.session_state.project_data["internal_loads"]["equipment"] = [
1189
- s for s in equipment_systems if s.get("id") != system_id
1190
- ]
1191
- st.success(f"Equipment '{system_name}' deleted!")
1192
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1193
- st.session_state.module_rerun_flags["internal_loads"] = True
1194
- st.rerun()
1195
 
1196
  def display_ventilation_infiltration_table(vent_inf_systems: List[Dict[str, Any]]):
1197
  """Display ventilation/infiltration systems in a table format with edit/delete buttons."""
1198
- if not vent_inf_systems:
1199
- return
 
 
 
 
 
 
 
1200
 
1201
- # Create table data
1202
- table_data = []
1203
- for system in vent_inf_systems:
1204
- if system.get("system_type") == "Ventilation":
1205
- rate_info = f"{system.get('ventilation_rate', 0):.2f} L/s路m虏"
1206
- else:
1207
- rate_info = f"{system.get('air_change_rate', 0):.2f} ACH"
 
 
1208
 
1209
- table_data.append({
1210
- "Name": system.get("name", "Unknown"),
1211
- "Type": system.get("system_type", "Unknown"),
1212
- "Area (m虏)": f"{system.get('area', 0):.1f}",
1213
- "Rate": rate_info,
1214
- "Zone": system.get("zone", "Unknown"),
1215
- "Schedule": system.get("schedule", "Unknown"),
1216
- "Actions": system.get("id", "")
1217
- })
1218
-
1219
- if table_data:
1220
- df = pd.DataFrame(table_data)
1221
-
1222
- # Display table
1223
- st.dataframe(df.drop('Actions', axis=1), use_container_width=True)
 
 
1224
 
1225
- # Display action buttons
1226
- st.write("**Actions:**")
1227
- for i, row in enumerate(table_data):
1228
- system_id = row["Actions"]
1229
- system_name = row["Name"]
1230
-
1231
- col1, col2, col3 = st.columns([2, 1, 1])
1232
-
1233
- with col1:
1234
- st.write(f"{i+1}. {system_name}")
1235
-
1236
- with col2:
1237
- if st.button("Edit", key=f"edit_vent_inf_{system_id}_{i}"):
1238
- # Set up editor for editing
1239
- system = next((s for s in vent_inf_systems if s.get("id") == system_id), None)
1240
- if system:
1241
- st.session_state.vent_inf_editor = {
1242
- "is_edit": True,
1243
- "edit_id": system_id,
1244
- "name": system.get("name", ""),
1245
- "system_type": system.get("system_type", "Ventilation"),
1246
- "area": system.get("area", 100.0),
1247
- "ventilation_rate": system.get("ventilation_rate", 10.0),
1248
- "air_change_rate": system.get("air_change_rate", 1.0),
1249
- "schedule": system.get("schedule", "Continuous"),
1250
- "zone": system.get("zone", "Whole Building")
1251
- }
1252
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1253
- st.session_state.module_rerun_flags["internal_loads"] = True
1254
- st.rerun()
1255
-
1256
- with col3:
1257
- if st.button("Delete", key=f"delete_vent_inf_{system_id}_{i}"):
1258
- # Delete ventilation/infiltration system
1259
- st.session_state.project_data["internal_loads"]["ventilation_infiltration"] = [
1260
- s for s in vent_inf_systems if s.get("id") != system_id
1261
- ]
1262
- st.success(f"{system.get('system_type', 'System')} '{system_name}' deleted!")
1263
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1264
- st.session_state.module_rerun_flags["internal_loads"] = True
1265
- st.rerun()
1266
 
1267
  def display_schedules_table(schedules: Dict[str, Any]):
1268
  """Display schedules in a table format with edit/delete buttons."""
@@ -1301,13 +1192,11 @@ def display_schedules_table(schedules: Dict[str, Any]):
1301
 
1302
  with col2:
1303
  if st.button("View", key=f"view_schedule_{schedule_name}_{i}"):
1304
- # Display schedule chart
1305
  schedule_data = schedules[schedule_name]
1306
  display_schedule_chart(schedule_name, schedule_data)
1307
 
1308
  with col3:
1309
  if st.button("Edit", key=f"edit_schedule_{schedule_name}_{i}"):
1310
- # Set up editor for editing
1311
  schedule_data = schedules[schedule_name]
1312
  st.session_state.schedule_editor = {
1313
  "is_edit": True,
@@ -1317,22 +1206,18 @@ def display_schedules_table(schedules: Dict[str, Any]):
1317
  "weekday": schedule_data.get("weekday", [0.0] * 24),
1318
  "weekend": schedule_data.get("weekend", [0.0] * 24)
1319
  }
1320
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1321
  st.session_state.module_rerun_flags["internal_loads"] = True
1322
  st.rerun()
1323
 
1324
  with col4:
1325
- # [FIX]: Check if schedule is in use or a default template before allowing deletion
1326
  if schedule_name not in DEFAULT_SCHEDULE_TEMPLATES:
1327
  if is_schedule_in_use(schedule_name):
1328
  st.write("(In Use)")
1329
  else:
1330
  if st.button("Delete", key=f"delete_schedule_{schedule_name}_{i}"):
1331
- # Delete schedule
1332
  del schedules[schedule_name]
1333
  st.success(f"Schedule '{schedule_name}' deleted!")
1334
- # [FIX]: Use module_rerun_flags instead of internal_loads_rerun_pending
1335
  st.session_state.module_rerun_flags["internal_loads"] = True
1336
  st.rerun()
1337
  else:
1338
- st.write("(Default)")
 
3
 
4
  This module handles the internal loads functionality of the BuildSustain application,
5
  allowing users to define occupancy, lighting, equipment, ventilation, infiltration, and schedules.
6
+ It provides comprehensive load management with a UI structure matching the Components module.
7
 
8
  Developed by: Dr Majed Abuseif, Deakin University
9
  漏 2025
 
49
  with st.expander("Help & Information"):
50
  display_internal_loads_help()
51
 
52
+ # Initialize rerun flags if not present
53
+ if "module_rerun_flags" not in st.session_state:
54
+ st.session_state.module_rerun_flags = {}
55
+
56
  # Check if rerun is pending
 
57
  if st.session_state.module_rerun_flags.get("internal_loads", False):
58
  st.session_state.module_rerun_flags["internal_loads"] = False
59
  st.rerun()
 
90
  def initialize_internal_loads():
91
  """
92
  Initialize internal loads in session state if not present.
 
93
  Logging a warning if initialization is needed for debugging.
94
  """
95
  if "internal_loads" not in st.session_state.project_data:
 
103
  }
104
 
105
  def display_people_tab():
106
+ """Display the people tab content with a two-column layout similar to components.py."""
107
+ # Get people from session state
108
+ people_groups = st.session_state.project_data["internal_loads"]["people"]
109
+
110
  # Split the display into two columns
111
  col1, col2 = st.columns([3, 2])
112
 
113
  with col1:
114
  st.subheader("Saved People Groups")
 
 
 
 
115
  if people_groups:
116
  display_people_table(people_groups)
117
  else:
 
120
  with col2:
121
  st.subheader("People Group Editor/Creator")
122
 
123
+ # Initialize editor and action states
124
  if "people_editor" not in st.session_state:
125
  st.session_state.people_editor = {}
126
+ if "people_action" not in st.session_state:
127
+ st.session_state.people_action = {"action": None, "id": None}
128
 
129
+ # Display the editor form
130
  with st.form("people_editor_form", clear_on_submit=True):
131
  editor_state = st.session_state.get("people_editor", {})
132
  is_edit = editor_state.get("is_edit", False)
 
198
  # Submit buttons
199
  col1, col2 = st.columns(2)
200
  with col1:
201
+ submit_label = "Update" if is_edit else "Add"
202
+ submit = st.form_submit_button(f"{submit_label} People Group")
203
 
204
  with col2:
205
+ refresh = st.form_submit_button("Refresh")
206
 
207
  # Handle form submission
208
  if submit:
209
  # Validate inputs
210
  if not name.strip():
211
  st.error("Group name is required.")
212
+ return
213
+ # Check for unique name
214
+ existing_names = [group["name"] for group in people_groups if not (is_edit and group["name"] == editor_state.get("name"))]
215
+ if name.strip() in existing_names:
216
+ st.error("Group name must be unique.")
217
+ return
218
+
219
+ # Get activity data
220
+ activity_data = PEOPLE_ACTIVITY_LEVELS[activity_level]
221
+
222
+ # Create people group data
223
+ people_data = {
224
+ "id": str(uuid.uuid4()),
225
+ "name": name.strip(),
226
+ "num_people": num_people,
227
+ "activity_level": activity_level,
228
+ "activity_data": activity_data,
229
+ "clo_summer": clo_summer,
230
+ "clo_winter": clo_winter,
231
+ "schedule": schedule,
232
+ "zone": zone,
233
+ "sensible_heat_per_person": (activity_data["sensible_min_w"] + activity_data["sensible_max_w"]) / 2,
234
+ "latent_heat_per_person": (activity_data["latent_min_w"] + activity_data["latent_max_w"]) / 2,
235
+ "total_sensible_heat": num_people * (activity_data["sensible_min_w"] + activity_data["sensible_max_w"]) / 2,
236
+ "total_latent_heat": num_people * (activity_data["latent_min_w"] + activity_data["latent_max_w"]) / 2
237
+ }
238
+
239
+ # Handle edit mode
240
+ if is_edit:
241
+ index = editor_state.get("index", 0)
242
+ st.session_state.project_data["internal_loads"]["people"][index] = people_data
243
+ st.success(f"People group '{name}' updated!")
244
  else:
245
+ st.session_state.project_data["internal_loads"]["people"].append(people_data)
246
+ st.success(f"People group '{name}' added!")
247
+
248
+ # Clear editor state
249
+ st.session_state.people_editor = {}
250
+ st.session_state.people_action = {"action": "save", "id": str(uuid.uuid4())}
251
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
+ elif refresh:
254
  # Clear editor state
255
  st.session_state.people_editor = {}
256
+ st.session_state.people_action = {"action": "refresh", "id": str(uuid.uuid4())}
257
  st.session_state.module_rerun_flags["internal_loads"] = True
 
258
 
259
  def display_lighting_tab():
260
+ """Display the lighting tab content with a two-column layout similar to components.py."""
261
+ # Get lighting from session state
262
+ lighting_systems = st.session_state.project_data["internal_loads"]["lighting"]
263
+
264
  # Split the display into two columns
265
  col1, col2 = st.columns([3, 2])
266
 
267
  with col1:
268
  st.subheader("Saved Lighting Systems")
 
 
 
 
269
  if lighting_systems:
270
  display_lighting_table(lighting_systems)
271
  else:
 
274
  with col2:
275
  st.subheader("Lighting System Editor/Creator")
276
 
277
+ # Initialize editor and action states
278
  if "lighting_editor" not in st.session_state:
279
  st.session_state.lighting_editor = {}
280
+ if "lighting_action" not in st.session_state:
281
+ st.session_state.lighting_action = {"action": None, "id": None}
282
 
283
  # Get building type for default values
284
  building_type = st.session_state.project_data["building_info"].get("building_type")
285
  default_lighting_density = DEFAULT_BUILDING_INTERNALS.get(building_type, DEFAULT_BUILDING_INTERNALS["Other"])["lighting_density"]
286
 
287
+ # Display the editor form
288
  with st.form("lighting_editor_form", clear_on_submit=True):
289
  editor_state = st.session_state.get("lighting_editor", {})
290
  is_edit = editor_state.get("is_edit", False)
 
297
  )
298
 
299
  # Area
 
300
  area = st.number_input(
301
  "Area (m虏)",
302
  min_value=1.0,
 
340
  help="Fraction of heat released as convection."
341
  )
342
 
 
 
 
 
343
  # Schedule selection
344
  available_schedules = list(st.session_state.project_data["internal_loads"]["schedules"].keys())
345
  schedule = st.selectbox(
 
359
  # Submit buttons
360
  col1, col2 = st.columns(2)
361
  with col1:
362
+ submit_label = "Update" if is_edit else "Add"
363
+ submit = st.form_submit_button(f"{submit_label} Lighting System")
364
 
365
  with col2:
366
+ refresh = st.form_submit_button("Refresh")
367
 
368
  # Handle form submission
369
  if submit:
370
  # Validate inputs
371
  if not name.strip():
372
  st.error("System name is required.")
373
+ return
374
+ if abs(radiative_fraction + convective_fraction - 1.0) > 0.01:
375
  st.error("Radiative and convective fractions must sum to 1.0")
376
+ return
377
+ # Check for unique name
378
+ existing_names = [system["name"] for system in lighting_systems if not (is_edit and system["name"] == editor_state.get("name"))]
379
+ if name.strip() in existing_names:
380
+ st.error("System name must be unique.")
381
+ return
382
+
383
+ # Create lighting system data
384
+ lighting_data = {
385
+ "id": str(uuid.uuid4()),
386
+ "name": name.strip(),
387
+ "area": area,
388
+ "lpd": lpd,
389
+ "total_power": area * lpd,
390
+ "radiative_fraction": radiative_fraction,
391
+ "convective_fraction": convective_fraction,
392
+ "schedule": schedule,
393
+ "zone": zone
394
+ }
395
+
396
+ # Handle edit mode
397
+ if is_edit:
398
+ index = editor_state.get("index", 0)
399
+ st.session_state.project_data["internal_loads"]["lighting"][index] = lighting_data
400
+ st.success(f"Lighting system '{name}' updated!")
401
  else:
402
+ st.session_state.project_data["internal_loads"]["lighting"].append(lighting_data)
403
+ st.success(f"Lighting system '{name}' added!")
404
+
405
+ # Clear editor state
406
+ st.session_state.lighting_editor = {}
407
+ st.session_state.lighting_action = {"action": "save", "id": str(uuid.uuid4())}
408
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
+ elif refresh:
411
  # Clear editor state
412
  st.session_state.lighting_editor = {}
413
+ st.session_state.lighting_action = {"action": "refresh", "id": str(uuid.uuid4())}
414
  st.session_state.module_rerun_flags["internal_loads"] = True
 
415
 
416
  def display_equipment_tab():
417
+ """Display the equipment tab content with a two-column layout similar to components.py."""
418
+ # Get equipment from session state
419
+ equipment_systems = st.session_state.project_data["internal_loads"]["equipment"]
420
+
421
  # Split the display into two columns
422
  col1, col2 = st.columns([3, 2])
423
 
424
  with col1:
425
  st.subheader("Saved Equipment")
 
 
 
 
426
  if equipment_systems:
427
  display_equipment_table(equipment_systems)
428
  else:
 
431
  with col2:
432
  st.subheader("Equipment Editor/Creator")
433
 
434
+ # Initialize editor and action states
435
  if "equipment_editor" not in st.session_state:
436
  st.session_state.equipment_editor = {}
437
+ if "equipment_action" not in st.session_state:
438
+ st.session_state.equipment_action = {"action": None, "id": None}
439
 
440
  # Get building type for default values
441
  building_type = st.session_state.project_data["building_info"].get("building_type")
442
  default_equipment_data = DEFAULT_BUILDING_INTERNALS.get(building_type, DEFAULT_BUILDING_INTERNALS["Other"])["equipment_heat_gains"]
443
 
444
+ # Display the editor form
445
  with st.form("equipment_editor_form", clear_on_submit=True):
446
  editor_state = st.session_state.get("equipment_editor", {})
447
  is_edit = editor_state.get("is_edit", False)
 
454
  )
455
 
456
  # Area
 
457
  area = st.number_input(
458
  "Area (m虏)",
459
  min_value=1.0,
 
511
  help="Fraction of sensible heat released as convection."
512
  )
513
 
 
 
 
 
514
  # Schedule selection
515
  available_schedules = list(st.session_state.project_data["internal_loads"]["schedules"].keys())
516
  schedule = st.selectbox(
 
530
  # Submit buttons
531
  col1, col2 = st.columns(2)
532
  with col1:
533
+ submit_label = "Update" if is_edit else "Add"
534
+ submit = st.form_submit_button(f"{submit_label} Equipment")
535
 
536
  with col2:
537
+ refresh = st.form_submit_button("Refresh")
538
 
539
  # Handle form submission
540
  if submit:
541
  # Validate inputs
542
  if not name.strip():
543
  st.error("Equipment name is required.")
544
+ return
545
+ if abs(radiative_fraction + convective_fraction - 1.0) > 0.01:
546
  st.error("Radiative and convective fractions must sum to 1.0")
547
+ return
548
+ # Check for unique name
549
+ existing_names = [system["name"] for system in equipment_systems if not (is_edit and system["name"] == editor_state.get("name"))]
550
+ if name.strip() in existing_names:
551
+ st.error("Equipment name must be unique.")
552
+ return
553
+
554
+ # Create equipment data
555
+ equipment_data = {
556
+ "id": str(uuid.uuid4()),
557
+ "name": name.strip(),
558
+ "area": area,
559
+ "sensible_gain": sensible_gain,
560
+ "latent_gain": latent_gain,
561
+ "total_sensible_power": area * sensible_gain,
562
+ "total_latent_power": area * latent_gain,
563
+ "radiative_fraction": radiative_fraction,
564
+ "convective_fraction": convective_fraction,
565
+ "schedule": schedule,
566
+ "zone": zone
567
+ }
568
+
569
+ # Handle edit mode
570
+ if is_edit:
571
+ index = editor_state.get("index", 0)
572
+ st.session_state.project_data["internal_loads"]["equipment"][index] = equipment_data
573
+ st.success(f"Equipment '{name}' updated!")
574
  else:
575
+ st.session_state.project_data["internal_loads"]["equipment"].append(equipment_data)
576
+ st.success(f"Equipment '{name}' added!")
577
+
578
+ # Clear editor state
579
+ st.session_state.equipment_editor = {}
580
+ st.session_state.equipment_action = {"action": "save", "id": str(uuid.uuid4())}
581
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
 
583
+ elif refresh:
584
  # Clear editor state
585
  st.session_state.equipment_editor = {}
586
+ st.session_state.equipment_action = {"action": "refresh", "id": str(uuid.uuid4())}
587
  st.session_state.module_rerun_flags["internal_loads"] = True
 
588
 
589
  def display_ventilation_infiltration_tab():
590
+ """Display the ventilation/infiltration tab content with a two-column layout similar to components.py."""
591
+ # Get ventilation/infiltration from session state
592
+ vent_inf_systems = st.session_state.project_data["internal_loads"]["ventilation_infiltration"]
593
+
594
  # Split the display into two columns
595
  col1, col2 = st.columns([3, 2])
596
 
597
  with col1:
598
  st.subheader("Saved Ventilation & Infiltration")
 
 
 
 
599
  if vent_inf_systems:
600
  display_ventilation_infiltration_table(vent_inf_systems)
601
  else:
 
604
  with col2:
605
  st.subheader("Ventilation/Infiltration Editor/Creator")
606
 
607
+ # Initialize editor and action states
608
  if "vent_inf_editor" not in st.session_state:
609
  st.session_state.vent_inf_editor = {}
610
+ if "vent_inf_action" not in st.session_state:
611
+ st.session_state.vent_inf_action = {"action": None, "id": None}
612
 
613
  # Get building type for default values
614
  building_type = st.session_state.project_data["building_info"].get("building_type")
615
  default_building_data = DEFAULT_BUILDING_INTERNALS.get(building_type, DEFAULT_BUILDING_INTERNALS["Other"])
616
 
617
+ # Display the editor form
618
  with st.form("vent_inf_editor_form", clear_on_submit=True):
619
  editor_state = st.session_state.get("vent_inf_editor", {})
620
  is_edit = editor_state.get("is_edit", False)
 
635
  )
636
 
637
  # Area
 
638
  area = st.number_input(
639
  "Area (m虏)",
640
  min_value=1.0,
 
654
  format="%.2f",
655
  help="Ventilation rate in liters per second per square meter."
656
  )
657
+ air_change_rate = 0.0
 
658
  else:
659
  # Air change rate for infiltration
660
  air_change_rate = st.number_input(
 
665
  format="%.2f",
666
  help="Air change rate in air changes per hour."
667
  )
668
+ ventilation_rate = 0.0
 
669
 
670
  # Schedule selection
671
  available_schedules = list(st.session_state.project_data["internal_loads"]["schedules"].keys())
 
686
  # Submit buttons
687
  col1, col2 = st.columns(2)
688
  with col1:
689
+ submit_label = "Update" if is_edit else "Add"
690
+ submit = st.form_submit_button(f"{submit_label} System")
691
 
692
  with col2:
693
+ refresh = st.form_submit_button("Refresh")
694
 
695
  # Handle form submission
696
  if submit:
697
  # Validate inputs
698
  if not name.strip():
699
  st.error("System name is required.")
700
+ return
701
+ # Check for unique name
702
+ existing_names = [system["name"] for system in vent_inf_systems if not (is_edit and system["name"] == editor_state.get("name"))]
703
+ if name.strip() in existing_names:
704
+ st.error("System name must be unique.")
705
+ return
706
+
707
+ # Create ventilation/infiltration data
708
+ vent_inf_data = {
709
+ "id": str(uuid.uuid4()),
710
+ "name": name.strip(),
711
+ "system_type": system_type,
712
+ "area": area,
713
+ "ventilation_rate": ventilation_rate,
714
+ "air_change_rate": air_change_rate,
715
+ "schedule": schedule,
716
+ "zone": zone
717
+ }
718
+
719
+ # Handle edit mode
720
+ if is_edit:
721
+ index = editor_state.get("index", 0)
722
+ st.session_state.project_data["internal_loads"]["ventilation_infiltration"][index] = vent_inf_data
723
+ st.success(f"{system_type} system '{name}' updated!")
724
  else:
725
+ st.session_state.project_data["internal_loads"]["ventilation_infiltration"].append(vent_inf_data)
726
+ st.success(f"{system_type} system '{name}' added!")
727
+
728
+ # Clear editor state
729
+ st.session_state.vent_inf_editor = {}
730
+ st.session_state.vent_inf_action = {"action": "save", "id": str(uuid.uuid4())}
731
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732
 
733
+ elif refresh:
734
  # Clear editor state
735
  st.session_state.vent_inf_editor = {}
736
+ st.session_state.vent_inf_action = {"action": "refresh", "id": str(uuid.uuid4())}
737
  st.session_state.module_rerun_flags["internal_loads"] = True
 
738
 
739
  def display_schedules_tab():
740
  import streamlit as st
 
761
  editor_state = st.session_state.schedule_editor
762
  schedules = st.session_state.project_data["internal_loads"]["schedules"]
763
 
764
+ # Handle template change
765
  template_options = list(DEFAULT_SCHEDULE_TEMPLATES.keys())
766
  selected_template = st.selectbox(
767
  "Select Template",
 
778
  tpl = DEFAULT_SCHEDULE_TEMPLATES[selected_template]
779
  st.session_state.schedule_editor["weekday"] = tpl["weekday"]
780
  st.session_state.schedule_editor["weekend"] = tpl["weekend"]
 
781
  for hour in range(24):
782
  st.session_state[f"weekday_slider_{hour}_value"] = tpl["weekday"][hour]
783
  st.session_state[f"weekend_slider_{hour}_value"] = tpl["weekend"][hour]
 
788
  st.session_state.module_rerun_flags["internal_loads"] = True
789
  st.rerun()
790
 
791
+ # UI FORM for name/description and actions
792
  with st.form("schedule_form"):
793
  name = st.text_input("Schedule Name", value=editor_state.get("name", ""))
794
  description = st.text_area("Description", value=editor_state.get("description", ""))
795
 
796
+ # SLIDERS LAYOUT
797
  st.markdown("### Schedule Sliders")
 
 
798
  col_hour, col_wd, col_we = st.columns([0.4, 2.0, 2.0])
799
  with col_hour:
800
  st.markdown("<div style='text-align:center; font-weight:bold; font-size:18px;'>Hour</div>", unsafe_allow_html=True)
 
818
 
819
  for hour in range(24):
820
  col_hour, col_wd, col_we = st.columns([0.4, 2.0, 2.0])
 
821
  with col_hour:
822
  st.markdown(f"<div style='text-align:center; font-size:12px'>{hour:02d}</div>", unsafe_allow_html=True)
 
823
  with col_wd:
824
  val = st.slider(
825
  label=f"Weekday {hour:02d}",
 
832
  format=None
833
  )
834
  weekday_values.append(val)
 
835
  with col_we:
836
  val = st.slider(
837
  label=f"Weekend {hour:02d}",
 
845
  )
846
  weekend_values.append(val)
847
 
848
+ # Action Buttons
849
  col1, col2, col3 = st.columns(3)
850
  with col1:
851
  submit_label = "Update Schedule" if editor_state.get("is_edit") else "Save Schedule"
 
855
  with col3:
856
  delete_submitted = st.form_submit_button("Delete Selected Schedule")
857
 
858
+ # Saved Schedules Selector
859
  st.markdown("### Saved Schedules")
860
  saved_schedule = st.selectbox(
861
  "Select Saved Schedule",
 
864
  help="Select a saved schedule to edit or delete."
865
  )
866
 
867
+ # Save logic
868
  if submitted:
869
  action_id = str(uuid.uuid4())
870
  if st.session_state.schedule_action.get("id") != action_id:
 
882
  if editor_state.get("is_edit") and editor_state.get("original_name") and editor_state.get("original_name") != name:
883
  if editor_state["original_name"] in schedules:
884
  del schedules[editor_state["original_name"]]
 
885
  st.session_state.schedule_editor = DEFAULT_STATE.copy()
886
  for hour in range(24):
887
  st.session_state[f"weekday_slider_{hour}_value"] = 0.0
 
891
  st.session_state.module_rerun_flags["internal_loads"] = True
892
  st.rerun()
893
 
894
+ # Edit logic
895
  if edit_submitted and saved_schedule:
896
  action_id = str(uuid.uuid4())
897
  if st.session_state.schedule_action.get("id") != action_id:
 
900
  st.error("Default schedules cannot be edited.")
901
  else:
902
  schedule_data = schedules[saved_schedule]
 
903
  st.session_state.schedule_editor = {
904
  "is_edit": True,
905
  "original_name": saved_schedule,
 
909
  "weekend": schedule_data.get("weekend", [0.0] * 24),
910
  "template": "None"
911
  }
 
912
  for hour in range(24):
913
  st.session_state[f"weekday_slider_{hour}_value"] = schedule_data.get("weekday", [0.0] * 24)[hour]
914
  st.session_state[f"weekend_slider_{hour}_value"] = schedule_data.get("weekend", [0.0] * 24)[hour]
 
916
  st.session_state.module_rerun_flags["internal_loads"] = True
917
  st.rerun()
918
 
919
+ # Delete logic
920
  if delete_submitted and saved_schedule:
921
  action_id = str(uuid.uuid4())
922
  if st.session_state.schedule_action.get("id") != action_id:
 
927
  st.error(f"Schedule '{saved_schedule}' is in use and cannot be deleted.")
928
  else:
929
  del schedules[saved_schedule]
 
930
  st.session_state.schedule_editor = DEFAULT_STATE.copy()
931
  for hour in range(24):
932
  st.session_state[f"weekday_slider_{hour}_value"] = 0.0
 
939
  def is_schedule_in_use(schedule_name: str) -> bool:
940
  """
941
  Check if a schedule is in use by any people, lighting, equipment, or ventilation/infiltration systems.
 
942
 
943
  Args:
944
  schedule_name: Name of the schedule to check.
 
963
 
964
  def display_people_table(people_groups: List[Dict[str, Any]]):
965
  """Display people groups in a table format with edit/delete buttons."""
966
+ # Create column headers
967
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
968
+ cols[0].write("**Name**")
969
+ cols[1].write("**Number**")
970
+ cols[2].write("**Sensible (W)**")
971
+ cols[3].write("**Latent (W)**")
972
+ cols[4].write("**Zone**")
973
+ cols[5].write("**Edit**")
974
+ cols[6].write("**Delete**")
 
 
 
 
 
 
 
975
 
976
+ # Display each group
977
+ for idx, group in enumerate(people_groups):
978
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
979
+ cols[0].write(group["name"])
980
+ cols[1].write(str(group.get("num_people", 0)))
981
+ cols[2].write(f"{group.get('total_sensible_heat', 0):.1f}")
982
+ cols[3].write(f"{group.get('total_latent_heat', 0):.1f}")
983
+ cols[4].write(group.get("zone", "Unknown"))
984
 
985
+ # Edit button
986
+ edit_key = f"edit_people_{group['id']}_{idx}"
987
+ with cols[5].container():
988
+ if st.button("Edit", key=edit_key):
989
+ st.session_state.people_editor = {
990
+ "index": idx,
991
+ "name": group.get("name", ""),
992
+ "num_people": group.get("num_people", 10),
993
+ "activity_level": group.get("activity_level", list(PEOPLE_ACTIVITY_LEVELS.keys())[0]),
994
+ "clo_summer": group.get("clo_summer", 0.5),
995
+ "clo_winter": group.get("clo_winter", 1.0),
996
+ "schedule": group.get("schedule", "Continuous"),
997
+ "zone": group.get("zone", "Whole Building"),
998
+ "is_edit": True
999
+ }
1000
+ st.session_state.people_action = {"action": "edit", "id": str(uuid.uuid4())}
1001
+ st.session_state.module_rerun_flags["internal_loads"] = True
1002
 
1003
+ # Delete button
1004
+ delete_key = f"delete_people_{group['id']}_{idx}"
1005
+ with cols[6].container():
1006
+ if st.button("Delete", key=delete_key):
1007
+ st.session_state.project_data["internal_loads"]["people"].pop(idx)
1008
+ st.success(f"People group '{group['name']}' deleted!")
1009
+ st.session_state.people_action = {"action": "delete", "id": str(uuid.uuid4())}
1010
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1011
 
1012
  def display_lighting_table(lighting_systems: List[Dict[str, Any]]):
1013
  """Display lighting systems in a table format with edit/delete buttons."""
1014
+ # Create column headers
1015
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
1016
+ cols[0].write("**Name**")
1017
+ cols[1].write("**Area (m虏)**")
1018
+ cols[2].write("**LPD (W/m虏)**")
1019
+ cols[3].write("**Total Power (W)**")
1020
+ cols[4].write("**Zone**")
1021
+ cols[5].write("**Edit**")
1022
+ cols[6].write("**Delete**")
1023
 
1024
+ # Display each system
1025
+ for idx, system in enumerate(lighting_systems):
1026
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
1027
+ cols[0].write(system["name"])
1028
+ cols[1].write(f"{system.get('area', 0):.1f}")
1029
+ cols[2].write(f"{system.get('lpd', 0):.2f}")
1030
+ cols[3].write(f"{system.get('total_power', 0):.1f}")
1031
+ cols[4].write(system.get("zone", "Unknown"))
 
 
 
 
 
 
 
1032
 
1033
+ # Edit button
1034
+ edit_key = f"edit_lighting_{system['id']}_{idx}"
1035
+ with cols[5].container():
1036
+ if st.button("Edit", key=edit_key):
1037
+ st.session_state.lighting_editor = {
1038
+ "index": idx,
1039
+ "name": system.get("name", ""),
1040
+ "area": system.get("area", 100.0),
1041
+ "lpd": system.get("lpd", 12.0),
1042
+ "radiative_fraction": system.get("radiative_fraction", 0.4),
1043
+ "convective_fraction": system.get("convective_fraction", 0.6),
1044
+ "schedule": system.get("schedule", "Continuous"),
1045
+ "zone": system.get("zone", "Whole Building"),
1046
+ "is_edit": True
1047
+ }
1048
+ st.session_state.lighting_action = {"action": "edit", "id": str(uuid.uuid4())}
1049
+ st.session_state.module_rerun_flags["internal_loads"] = True
1050
 
1051
+ # Delete button
1052
+ delete_key = f"delete_lighting_{system['id']}_{idx}"
1053
+ with cols[6].container():
1054
+ if st.button("Delete", key=delete_key):
1055
+ st.session_state.project_data["internal_loads"]["lighting"].pop(idx)
1056
+ st.success(f"Lighting system '{system['name']}' deleted!")
1057
+ st.session_state.lighting_action = {"action": "delete", "id": str(uuid.uuid4())}
1058
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1059
 
1060
  def display_equipment_table(equipment_systems: List[Dict[str, Any]]):
1061
  """Display equipment systems in a table format with edit/delete buttons."""
1062
+ # Create column headers
1063
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
1064
+ cols[0].write("**Name**")
1065
+ cols[1].write("**Area (m虏)**")
1066
+ cols[2].write("**Sensible (W)**")
1067
+ cols[3].write("**Latent (W)**")
1068
+ cols[4].write("**Zone**")
1069
+ cols[5].write("**Edit**")
1070
+ cols[6].write("**Delete**")
1071
 
1072
+ # Display each system
1073
+ for idx, system in enumerate(equipment_systems):
1074
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
1075
+ cols[0].write(system["name"])
1076
+ cols[1].write(f"{system.get('area', 0):.1f}")
1077
+ cols[2].write(f"{system.get('total_sensible_power', 0):.1f}")
1078
+ cols[3].write(f"{system.get('total_latent_power', 0):.1f}")
1079
+ cols[4].write(system.get("zone", "Unknown"))
 
 
 
 
 
 
 
1080
 
1081
+ # Edit button
1082
+ edit_key = f"edit_equipment_{system['id']}_{idx}"
1083
+ with cols[5].container():
1084
+ if st.button("Edit", key=edit_key):
1085
+ st.session_state.equipment_editor = {
1086
+ "index": idx,
1087
+ "name": system.get("name", ""),
1088
+ "area": system.get("area", 100.0),
1089
+ "sensible_gain": system.get("sensible_gain", 10.0),
1090
+ "latent_gain": system.get("latent_gain", 2.0),
1091
+ "radiative_fraction": system.get("radiative_fraction", 0.5),
1092
+ "convective_fraction": system.get("convective_fraction", 0.5),
1093
+ "schedule": system.get("schedule", "Continuous"),
1094
+ "zone": system.get("zone", "Whole Building"),
1095
+ "is_edit": True
1096
+ }
1097
+ st.session_state.equipment_action = {"action": "edit", "id": str(uuid.uuid4())}
1098
+ st.session_state.module_rerun_flags["internal_loads"] = True
1099
 
1100
+ # Delete button
1101
+ delete_key = f"delete_equipment_{system['id']}_{idx}"
1102
+ with cols[6].container():
1103
+ if st.button("Delete", key=delete_key):
1104
+ st.session_state.project_data["internal_loads"]["equipment"].pop(idx)
1105
+ st.success(f"Equipment '{system['name']}' deleted!")
1106
+ st.session_state.equipment_action = {"action": "delete", "id": str(uuid.uuid4())}
1107
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1108
 
1109
  def display_ventilation_infiltration_table(vent_inf_systems: List[Dict[str, Any]]):
1110
  """Display ventilation/infiltration systems in a table format with edit/delete buttons."""
1111
+ # Create column headers
1112
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
1113
+ cols[0].write("**Name**")
1114
+ cols[1].write("**Type**")
1115
+ cols[2].write("**Area (m虏)**")
1116
+ cols[3].write("**Rate**")
1117
+ cols[4].write("**Zone**")
1118
+ cols[5].write("**Edit**")
1119
+ cols[6].write("**Delete**")
1120
 
1121
+ # Display each system
1122
+ for idx, system in enumerate(vent_inf_systems):
1123
+ cols = st.columns([2, 1, 1, 1, 1, 1, 1])
1124
+ cols[0].write(system["name"])
1125
+ cols[1].write(system.get("system_type", "Unknown"))
1126
+ cols[2].write(f"{system.get('area', 0):.1f}")
1127
+ rate_info = f"{system.get('ventilation_rate', 0):.2f} L/s路m虏" if system.get("system_type") == "Ventilation" else f"{system.get('air_change_rate', 0):.2f} ACH"
1128
+ cols[3].write(rate_info)
1129
+ cols[4].write(system.get("zone", "Unknown"))
1130
 
1131
+ # Edit button
1132
+ edit_key = f"edit_vent_inf_{system['id']}_{idx}"
1133
+ with cols[5].container():
1134
+ if st.button("Edit", key=edit_key):
1135
+ st.session_state.vent_inf_editor = {
1136
+ "index": idx,
1137
+ "name": system.get("name", ""),
1138
+ "system_type": system.get("system_type", "Ventilation"),
1139
+ "area": system.get("area", 100.0),
1140
+ "ventilation_rate": system.get("ventilation_rate", 10.0),
1141
+ "air_change_rate": system.get("air_change_rate", 1.0),
1142
+ "schedule": system.get("schedule", "Continuous"),
1143
+ "zone": system.get("zone", "Whole Building"),
1144
+ "is_edit": True
1145
+ }
1146
+ st.session_state.vent_inf_action = {"action": "edit", "id": str(uuid.uuid4())}
1147
+ st.session_state.module_rerun_flags["internal_loads"] = True
1148
 
1149
+ # Delete button
1150
+ delete_key = f"delete_vent_inf_{system['id']}_{idx}"
1151
+ with cols[6].container():
1152
+ if st.button("Delete", key=delete_key):
1153
+ st.session_state.project_data["internal_loads"]["ventilation_infiltration"].pop(idx)
1154
+ st.success(f"{system.get('system_type', 'System')} '{system['name']}' deleted!")
1155
+ st.session_state.vent_inf_action = {"action": "delete", "id": str(uuid.uuid4())}
1156
+ st.session_state.module_rerun_flags["internal_loads"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1157
 
1158
  def display_schedules_table(schedules: Dict[str, Any]):
1159
  """Display schedules in a table format with edit/delete buttons."""
 
1192
 
1193
  with col2:
1194
  if st.button("View", key=f"view_schedule_{schedule_name}_{i}"):
 
1195
  schedule_data = schedules[schedule_name]
1196
  display_schedule_chart(schedule_name, schedule_data)
1197
 
1198
  with col3:
1199
  if st.button("Edit", key=f"edit_schedule_{schedule_name}_{i}"):
 
1200
  schedule_data = schedules[schedule_name]
1201
  st.session_state.schedule_editor = {
1202
  "is_edit": True,
 
1206
  "weekday": schedule_data.get("weekday", [0.0] * 24),
1207
  "weekend": schedule_data.get("weekend", [0.0] * 24)
1208
  }
 
1209
  st.session_state.module_rerun_flags["internal_loads"] = True
1210
  st.rerun()
1211
 
1212
  with col4:
 
1213
  if schedule_name not in DEFAULT_SCHEDULE_TEMPLATES:
1214
  if is_schedule_in_use(schedule_name):
1215
  st.write("(In Use)")
1216
  else:
1217
  if st.button("Delete", key=f"delete_schedule_{schedule_name}_{i}"):
 
1218
  del schedules[schedule_name]
1219
  st.success(f"Schedule '{schedule_name}' deleted!")
 
1220
  st.session_state.module_rerun_flags["internal_loads"] = True
1221
  st.rerun()
1222
  else:
1223
+ st.write("(Default)")