mabuseif commited on
Commit
8489a99
·
verified ·
1 Parent(s): 618d4ef

Update app/hvac_loads.py

Browse files
Files changed (1) hide show
  1. app/hvac_loads.py +115 -37
app/hvac_loads.py CHANGED
@@ -21,12 +21,16 @@ from utils.solar import SolarCalculations # Import SolarCalculations for SHGC d
21
  from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, DEFAULT_MATERIAL_PROPERTIES, DEFAULT_WINDOW_PROPERTIES
22
  import plotly.express as px
23
  import uuid
 
24
  run_id = str(uuid.uuid4())
25
 
26
  # Configure logging
27
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
28
  logger = logging.getLogger(__name__)
29
 
 
 
 
30
  class AdaptiveComfortModel:
31
  @staticmethod
32
  def compute_daily_mean_temperatures(hourly_data: List[Dict]) -> List[Tuple[Tuple[int, int], float]]:
@@ -461,9 +465,34 @@ class TFMCalculations:
461
  logger.error(f"Error calculating internal load for hour {hour}: {str(e)}")
462
  return 0.0
463
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  @staticmethod
465
  def calculate_ventilation_load(internal_loads: Dict, outdoor_temp: float, indoor_temp: float, area: float, building_info: Dict, hour: int, mode: str = "none") -> tuple[float, float]:
466
- """Calculate ventilation load for heating and cooling in kW based on mode."""
467
  if mode == "none":
468
  return 0, 0
469
  delta_t = outdoor_temp - indoor_temp
@@ -475,8 +504,13 @@ class TFMCalculations:
475
  total_cooling_load = 0.0
476
  total_heating_load = 0.0
477
  air_density = 1.2 # kg/m³
478
- specific_heat = 1000 # J/kg·K
479
  is_weekend = False # Simplified; determine from date in practice
 
 
 
 
 
480
 
481
  try:
482
  for system in internal_loads.get("ventilation", []):
@@ -485,36 +519,45 @@ class TFMCalculations:
485
  system_type = system.get("system_type", "AirChanges/Hour")
486
  system_area = system.get("area", area)
487
  ventilation_flow = 0.0
 
488
 
489
  if system_type == "AirChanges/Hour":
490
  design_flow_rate = system.get("design_flow_rate", 1.0) # L/s·m²
491
- ventilation_flow = design_flow_rate * system_area / 1000 # Convert L/s to m³/s
492
  if system.get("ventilation_type", "Natural") == "Mechanical":
493
  fan_power = (system.get("fan_pressure_rise", 200.0) * ventilation_flow) / system.get("fan_efficiency", 0.7) / 1000 # kW
494
- total_cooling_load += fan_power * fraction
495
- logger.debug(f"Ventilation '{system.get('name', 'unknown')}': fan_power={fan_power:.3f} kW, fraction={fraction:.2f}")
496
-
497
  elif system_type == "Wind and Stack Open Area":
498
  opening_effectiveness = system.get("opening_effectiveness", 50.0) / 100
499
- # Assume simplified flow based on area and effectiveness
500
  ventilation_flow = 0.001 * system_area * opening_effectiveness # m³/s (placeholder)
501
-
502
  elif system_type in ["Balanced Flow", "Heat Recovery"]:
503
  design_flow_rate = system.get("design_flow_rate", 1.0) # L/s·m²
504
- ventilation_flow = design_flow_rate * system_area / 1000 # Convert L/s to m³/s
505
  fan_power = (system.get("fan_pressure_rise", 200.0) * ventilation_flow) / system.get("fan_efficiency", 0.7) / 1000 # kW
506
- total_cooling_load += fan_power * fraction
507
  if system_type == "Heat Recovery":
508
  sensible_eff = system.get("sensible_effectiveness", 0.5)
509
  delta_t = delta_t * (1 - sensible_eff)
510
- logger.debug(f"Heat Recovery '{system.get('name', 'unknown')}': sensible_eff={sensible_eff:.2f}, adjusted_delta_t={delta_t:.2f}")
511
 
512
- load = ventilation_flow * air_density * specific_heat * delta_t * fraction / 1000 # kW
513
- cooling_load = load if mode == "cooling" else 0
514
- heating_load = -load if mode == "heating" else 0
 
 
 
 
 
 
 
 
 
 
 
515
  total_cooling_load += cooling_load
516
  total_heating_load += heating_load
517
- logger.debug(f"Ventilation '{system.get('name', 'unknown')}': flow={ventilation_flow:.4f} m³/s, fraction={fraction:.2f}, cooling={cooling_load:.3f} kW, heating={heating_load:.3f} kW")
 
 
 
 
518
 
519
  logger.info(f"Total ventilation load for hour {hour}: cooling={total_cooling_load:.3f} kW, heating={total_heating_load:.3f} kW")
520
  return total_cooling_load, total_heating_load
@@ -525,7 +568,7 @@ class TFMCalculations:
525
 
526
  @staticmethod
527
  def calculate_infiltration_load(internal_loads: Dict, outdoor_temp: float, indoor_temp: float, area: float, building_info: Dict, hour: int, mode: str = "none") -> tuple[float, float]:
528
- """Calculate infiltration load for heating and cooling in kW based on mode."""
529
  if mode == "none":
530
  return 0, 0
531
  delta_t = outdoor_temp - indoor_temp
@@ -537,10 +580,16 @@ class TFMCalculations:
537
  total_cooling_load = 0.0
538
  total_heating_load = 0.0
539
  air_density = 1.2 # kg/m³
540
- specific_heat = 1000 # J/kg·K
541
  building_height = building_info.get("building_height", 3.0)
542
  volume = area * building_height
543
  is_weekend = False # Simplified; determine from date in practice
 
 
 
 
 
 
544
 
545
  try:
546
  for system in internal_loads.get("infiltration", []):
@@ -552,36 +601,50 @@ class TFMCalculations:
552
 
553
  if system_type == "AirChanges/Hour":
554
  ach = system.get("design_flow_rate", 0.3)
555
- infiltration_flow = ach * system_area * building_height / 3600 # m³/s
556
-
 
 
557
  elif system_type == "Effective Leakage Area":
558
- ela = system.get("effective_air_leakage_area", 100.0) / 10000 # Convert cm² to m²
559
  stack_coeff = system.get("stack_coefficient", 0.0001)
560
  wind_coeff = system.get("wind_coefficient", 0.0001)
561
  delta_t_abs = abs(delta_t)
562
- wind_speed = 4.0 # m/s, assumed
563
  Q_stack = stack_coeff * ela * (delta_t_abs ** 0.5)
564
  Q_wind = wind_coeff * ela * (wind_speed ** 2)
565
- infiltration_flow = (Q_stack ** 2 + Q_wind ** 2) ** 0.5 # m³/s
566
-
567
  elif system_type == "Flow Coefficient":
568
- c = system.get("flow_coefficient", 0.0001) # m³/s·Paⁿ
569
  n = system.get("pressure_exponent", 0.6)
570
  stack_coeff = system.get("stack_coefficient", 0.0001)
571
  wind_coeff = system.get("wind_coefficient", 0.0001)
572
  delta_t_abs = abs(delta_t)
573
- wind_speed = 4.0 # m/s, assumed
574
  delta_p_stack = stack_coeff * delta_t_abs
575
  delta_p_wind = wind_coeff * (wind_speed ** 2)
576
  delta_p = (delta_p_stack ** 2 + delta_p_wind ** 2) ** 0.5
577
- infiltration_flow = c * (delta_p ** n) * system_area
578
-
579
- load = infiltration_flow * air_density * specific_heat * delta_t * fraction / 1000 # kW
580
- cooling_load = load if mode == "cooling" else 0
581
- heating_load = -load if mode == "heating" else 0
 
 
 
 
 
 
 
 
 
 
 
582
  total_cooling_load += cooling_load
583
  total_heating_load += heating_load
584
- logger.debug(f"Infiltration '{system.get('name', 'unknown')}': flow={infiltration_flow:.4f} m³/s, fraction={fraction:.2f}, cooling={cooling_load:.3f} kW, heating={heating_load:.3f} kW")
 
 
 
 
585
 
586
  logger.info(f"Total infiltration load for hour {hour}: cooling={total_cooling_load:.3f} kW, heating={total_heating_load:.3f} kW")
587
  return total_cooling_load, total_heating_load
@@ -898,8 +961,21 @@ def display_hvac_results_ui(loads: List[Dict[str, Any]], run_id: str = "default"
898
  if peak_cooling:
899
  st.write(f"**Peak Cooling Load**: {peak_cooling['total_cooling']:.2f} kW")
900
  st.write(f"Occurred on: {peak_cooling['month']}/{peak_cooling['day']} at {peak_cooling['hour']}:00")
 
 
 
 
 
 
 
 
 
901
  else:
902
  st.write("**Peak Cooling Load**: 0.00 kW")
 
 
 
 
903
 
904
  with col2:
905
  st.subheader("Heating Equipment Sizing")
@@ -1394,18 +1470,20 @@ def display_hvac_loads_page():
1394
 
1395
  # Store breakdown
1396
  cooling_breakdown = {
1397
- "conduction": sum(load["conduction_cooling"] for load in cooling_loads),
1398
- "solar": sum(load["solar"] for load in cooling_loads if load["total_cooling"] > 0),
1399
- "internal": sum(load["internal"] for load in cooling_loads if load["total_cooling"] > 0),
1400
- "ventilation": sum(load["ventilation_cooling"] for load in cooling_loads),
1401
- "infiltration": sum(load["infiltration_cooling"] for load in cooling_loads)
 
 
1402
  }
1403
  heating_breakdown = {
1404
  "conduction": sum(load["conduction_heating"] for load in heating_loads),
1405
  "ventilation": sum(load["ventilation_heating"] for load in heating_loads),
1406
  "infiltration": sum(load["infiltration_heating"] for load in heating_loads)
1407
  }
1408
- st.session_state.project_data["hvac_loads"]["cooling"]["breakdown"] = cooling_breakdown
1409
  st.session_state.project_data["hvac_loads"]["heating"]["breakdown"] = heating_breakdown
1410
 
1411
  # Display Results UI with unique run_id
 
21
  from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, DEFAULT_MATERIAL_PROPERTIES, DEFAULT_WINDOW_PROPERTIES
22
  import plotly.express as px
23
  import uuid
24
+ import psychrolib
25
  run_id = str(uuid.uuid4())
26
 
27
  # Configure logging
28
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
29
  logger = logging.getLogger(__name__)
30
 
31
+ # Initialize psychrolib for SI units
32
+ psychrolib.SetUnitSystem(psychrolib.SI)
33
+
34
  class AdaptiveComfortModel:
35
  @staticmethod
36
  def compute_daily_mean_temperatures(hourly_data: List[Dict]) -> List[Tuple[Tuple[int, int], float]]:
 
465
  logger.error(f"Error calculating internal load for hour {hour}: {str(e)}")
466
  return 0.0
467
 
468
+ @staticmethod
469
+ def calculate_enthalpy(temp_C: float, RH_percent: float, pressure_kPa: float = 101.325) -> float:
470
+ """Calculate air enthalpy (kJ/kg) using psychrometric properties."""
471
+ try:
472
+ # Clamp inputs to valid ranges
473
+ temp_C = max(-50.0, min(60.0, temp_C))
474
+ RH_percent = max(0.0, min(100.0, RH_percent))
475
+ pressure_kPa = max(50.0, min(120.0, pressure_kPa))
476
+
477
+ # Convert pressure to Pa for psychrolib
478
+ pressure_Pa = pressure_kPa * 1000.0
479
+
480
+ # Calculate humidity ratio (kg/kg)
481
+ W = psychrolib.GetHumRatioFromRelHum(temp_C, RH_percent / 100.0, pressure_Pa)
482
+
483
+ # Calculate enthalpy (kJ/kg)
484
+ h = psychrolib.GetMoistAirEnthalpy(temp_C, W)
485
+ h = h / 1000.0 # Convert J/kg to kJ/kg
486
+
487
+ logger.debug(f"Enthalpy calculated: temp={temp_C:.2f}°C, RH={RH_percent:.2f}%, pressure={pressure_kPa:.2f} kPa, h={h:.2f} kJ/kg")
488
+ return h
489
+ except Exception as e:
490
+ logger.error(f"Error calculating enthalpy: {str(e)}. Using default 50 kJ/kg.")
491
+ return 50.0 # Default enthalpy for 25°C, 50% RH
492
+
493
  @staticmethod
494
  def calculate_ventilation_load(internal_loads: Dict, outdoor_temp: float, indoor_temp: float, area: float, building_info: Dict, hour: int, mode: str = "none") -> tuple[float, float]:
495
+ """Calculate ventilation load (sensible and latent) for heating and cooling in kW based on mode."""
496
  if mode == "none":
497
  return 0, 0
498
  delta_t = outdoor_temp - indoor_temp
 
504
  total_cooling_load = 0.0
505
  total_heating_load = 0.0
506
  air_density = 1.2 # kg/m³
507
+ specific_heat = 1.006 # kJ/kg·K
508
  is_weekend = False # Simplified; determine from date in practice
509
+ climate_data = st.session_state.project_data.get("climate_data", {})
510
+ outdoor_rh = climate_data.get("hourly_data", [{}])[hour % 24].get("relative_humidity", 50.0)
511
+ indoor_rh = building_info.get("summer_indoor_design_rh" if mode == "cooling" else "winter_indoor_design_rh", 50.0)
512
+ altitude = climate_data.get("location", {}).get("elevation", 0.0)
513
+ pressure_kPa = 101.325 * (1 - 2.25577e-5 * altitude) ** 5.25588
514
 
515
  try:
516
  for system in internal_loads.get("ventilation", []):
 
519
  system_type = system.get("system_type", "AirChanges/Hour")
520
  system_area = system.get("area", area)
521
  ventilation_flow = 0.0
522
+ fan_power = 0.0
523
 
524
  if system_type == "AirChanges/Hour":
525
  design_flow_rate = system.get("design_flow_rate", 1.0) # L/s·m²
526
+ ventilation_flow = min(max(design_flow_rate * system_area / 1000, 0.0), system_area * 0.05) # m³/s, capped at 50 L/s·m²
527
  if system.get("ventilation_type", "Natural") == "Mechanical":
528
  fan_power = (system.get("fan_pressure_rise", 200.0) * ventilation_flow) / system.get("fan_efficiency", 0.7) / 1000 # kW
 
 
 
529
  elif system_type == "Wind and Stack Open Area":
530
  opening_effectiveness = system.get("opening_effectiveness", 50.0) / 100
 
531
  ventilation_flow = 0.001 * system_area * opening_effectiveness # m³/s (placeholder)
 
532
  elif system_type in ["Balanced Flow", "Heat Recovery"]:
533
  design_flow_rate = system.get("design_flow_rate", 1.0) # L/s·m²
534
+ ventilation_flow = min(max(design_flow_rate * system_area / 1000, 0.0), system_area * 0.05) # m³/s
535
  fan_power = (system.get("fan_pressure_rise", 200.0) * ventilation_flow) / system.get("fan_efficiency", 0.7) / 1000 # kW
 
536
  if system_type == "Heat Recovery":
537
  sensible_eff = system.get("sensible_effectiveness", 0.5)
538
  delta_t = delta_t * (1 - sensible_eff)
 
539
 
540
+ # Calculate enthalpies
541
+ h_out = TFMCalculations.calculate_enthalpy(outdoor_temp, outdoor_rh, pressure_kPa)
542
+ h_in = TFMCalculations.calculate_enthalpy(indoor_temp, indoor_rh, pressure_kPa)
543
+
544
+ # Total load (kW)
545
+ total_load = ventilation_flow * air_density * (h_out - h_in) * fraction / 1000
546
+ # Sensible load (kW)
547
+ sensible_load = ventilation_flow * air_density * specific_heat * delta_t * fraction / 1000
548
+ # Latent load (kW)
549
+ latent_load = total_load - sensible_load
550
+
551
+ # Assign loads based on mode
552
+ cooling_load = (sensible_load + latent_load + fan_power) if mode == "cooling" and total_load > 0 else 0
553
+ heating_load = -(sensible_load + latent_load) if mode == "heating" and total_load < 0 else 0
554
  total_cooling_load += cooling_load
555
  total_heating_load += heating_load
556
+
557
+ # Store sensible and latent components for UI
558
+ system["sensible_load"] = sensible_load
559
+ system["latent_load"] = latent_load
560
+ logger.debug(f"Ventilation '{system.get('name', 'unknown')}': flow={ventilation_flow:.4f} m³/s, fraction={fraction:.2f}, sensible={sensible_load:.3f} kW, latent={latent_load:.3f} kW, cooling={cooling_load:.3f} kW, heating={heating_load:.3f} kW")
561
 
562
  logger.info(f"Total ventilation load for hour {hour}: cooling={total_cooling_load:.3f} kW, heating={total_heating_load:.3f} kW")
563
  return total_cooling_load, total_heating_load
 
568
 
569
  @staticmethod
570
  def calculate_infiltration_load(internal_loads: Dict, outdoor_temp: float, indoor_temp: float, area: float, building_info: Dict, hour: int, mode: str = "none") -> tuple[float, float]:
571
+ """Calculate infiltration load (sensible and latent) for heating and cooling in kW based on mode."""
572
  if mode == "none":
573
  return 0, 0
574
  delta_t = outdoor_temp - indoor_temp
 
580
  total_cooling_load = 0.0
581
  total_heating_load = 0.0
582
  air_density = 1.2 # kg/m³
583
+ specific_heat = 1.006 # kJ/kg·K
584
  building_height = building_info.get("building_height", 3.0)
585
  volume = area * building_height
586
  is_weekend = False # Simplified; determine from date in practice
587
+ climate_data = st.session_state.project_data.get("climate_data", {})
588
+ wind_speed = climate_data.get("hourly_data", [{}])[hour % 24].get("wind_speed", 4.0)
589
+ outdoor_rh = climate_data.get("hourly_data", [{}])[hour % 24].get("relative_humidity", 50.0)
590
+ indoor_rh = building_info.get("summer_indoor_design_rh" if mode == "cooling" else "winter_indoor_design_rh", 50.0)
591
+ altitude = climate_data.get("location", {}).get("elevation", 0.0)
592
+ pressure_kPa = 101.325 * (1 - 2.25577e-5 * altitude) ** 5.25588
593
 
594
  try:
595
  for system in internal_loads.get("infiltration", []):
 
601
 
602
  if system_type == "AirChanges/Hour":
603
  ach = system.get("design_flow_rate", 0.3)
604
+ Q = ach * volume / 3600 # m³/s
605
+ wind_coeff = 0.23 # LBL model coefficient
606
+ ela = Q / (wind_coeff * max(wind_speed, 0.1) ** 2) if wind_speed > 0 else 0.0001 * system_area
607
+ infiltration_flow = wind_coeff * ela * (wind_speed ** 2) * fraction
608
  elif system_type == "Effective Leakage Area":
609
+ ela = system.get("effective_air_leakage_area", 100.0) / 10000 # cm² to m²
610
  stack_coeff = system.get("stack_coefficient", 0.0001)
611
  wind_coeff = system.get("wind_coefficient", 0.0001)
612
  delta_t_abs = abs(delta_t)
 
613
  Q_stack = stack_coeff * ela * (delta_t_abs ** 0.5)
614
  Q_wind = wind_coeff * ela * (wind_speed ** 2)
615
+ infiltration_flow = ((Q_stack ** 2 + Q_wind ** 2) ** 0.5) * fraction
 
616
  elif system_type == "Flow Coefficient":
617
+ c = system.get("flow_coefficient", 0.0001)
618
  n = system.get("pressure_exponent", 0.6)
619
  stack_coeff = system.get("stack_coefficient", 0.0001)
620
  wind_coeff = system.get("wind_coefficient", 0.0001)
621
  delta_t_abs = abs(delta_t)
 
622
  delta_p_stack = stack_coeff * delta_t_abs
623
  delta_p_wind = wind_coeff * (wind_speed ** 2)
624
  delta_p = (delta_p_stack ** 2 + delta_p_wind ** 2) ** 0.5
625
+ infiltration_flow = c * (delta_p ** n) * system_area * fraction
626
+
627
+ # Calculate enthalpies
628
+ h_out = TFMCalculations.calculate_enthalpy(outdoor_temp, outdoor_rh, pressure_kPa)
629
+ h_in = TFMCalculations.calculate_enthalpy(indoor_temp, indoor_rh, pressure_kPa)
630
+
631
+ # Total load (kW)
632
+ total_load = air_density * infiltration_flow * (h_out - h_in) / 1000
633
+ # Sensible load (kW)
634
+ sensible_load = air_density * infiltration_flow * specific_heat * delta_t / 1000
635
+ # Latent load (kW)
636
+ latent_load = total_load - sensible_load
637
+
638
+ # Assign loads based on mode
639
+ cooling_load = (sensible_load + latent_load) if mode == "cooling" and total_load > 0 else 0
640
+ heating_load = -(sensible_load + latent_load) if mode == "heating" and total_load < 0 else 0
641
  total_cooling_load += cooling_load
642
  total_heating_load += heating_load
643
+
644
+ # Store sensible and latent components for UI
645
+ system["sensible_load"] = sensible_load
646
+ system["latent_load"] = latent_load
647
+ logger.debug(f"Infiltration '{system.get('name', 'unknown')}': flow={infiltration_flow:.4f} m³/s, fraction={fraction:.2f}, sensible={sensible_load:.3f} kW, latent={latent_load:.3f} kW, cooling={cooling_load:.3f} kW, heating={heating_load:.3f} kW")
648
 
649
  logger.info(f"Total infiltration load for hour {hour}: cooling={total_cooling_load:.3f} kW, heating={total_heating_load:.3f} kW")
650
  return total_cooling_load, total_heating_load
 
961
  if peak_cooling:
962
  st.write(f"**Peak Cooling Load**: {peak_cooling['total_cooling']:.2f} kW")
963
  st.write(f"Occurred on: {peak_cooling['month']}/{peak_cooling['day']} at {peak_cooling['hour']}:00")
964
+ # Add ventilation and infiltration sensible/latent metrics
965
+ vent_sensible = sum(system.get("sensible_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("ventilation", []))
966
+ vent_latent = sum(system.get("latent_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("ventilation", []))
967
+ inf_sensible = sum(system.get("sensible_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("infiltration", []))
968
+ inf_latent = sum(system.get("latent_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("infiltration", []))
969
+ st.metric("Ventilation Sensible Cooling", f"{vent_sensible:.2f} kW")
970
+ st.metric("Ventilation Latent Cooling", f"{vent_latent:.2f} kW")
971
+ st.metric("Infiltration Sensible Cooling", f"{inf_sensible:.2f} kW")
972
+ st.metric("Infiltration Latent Cooling", f"{inf_latent:.2f} kW")
973
  else:
974
  st.write("**Peak Cooling Load**: 0.00 kW")
975
+ st.metric("Ventilation Sensible Cooling", "0.00 kW")
976
+ st.metric("Ventilation Latent Cooling", "0.00 kW")
977
+ st.metric("Infiltration Sensible Cooling", "0.00 kW")
978
+ st.metric("Infiltration Latent Cooling", "0.00 kW")
979
 
980
  with col2:
981
  st.subheader("Heating Equipment Sizing")
 
1470
 
1471
  # Store breakdown
1472
  cooling_breakdown = {
1473
+ "Conduction": sum(load["conduction_cooling"] for load in cooling_loads),
1474
+ "Solar Gains": sum(load["solar"] for load in cooling_loads),
1475
+ "Internal": sum(load["internal"] for load in cooling_loads),
1476
+ "Ventilation Sensible": sum(system.get("sensible_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("ventilation", [])),
1477
+ "Ventilation Latent": sum(system.get("latent_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("ventilation", [])),
1478
+ "Infiltration Sensible": sum(system.get("sensible_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("infiltration", [])),
1479
+ "Infiltration Latent": sum(system.get("latent_load", 0.0) for system in st.session_state.project_data["internal_loads"].get("infiltration", []))
1480
  }
1481
  heating_breakdown = {
1482
  "conduction": sum(load["conduction_heating"] for load in heating_loads),
1483
  "ventilation": sum(load["ventilation_heating"] for load in heating_loads),
1484
  "infiltration": sum(load["infiltration_heating"] for load in heating_loads)
1485
  }
1486
+ st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_component"] = cooling_breakdown
1487
  st.session_state.project_data["hvac_loads"]["heating"]["breakdown"] = heating_breakdown
1488
 
1489
  # Display Results UI with unique run_id