Spaces:
Sleeping
Sleeping
Update app/hvac_loads.py
Browse files- 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 =
|
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 #
|
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
|
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 |
-
|
513 |
-
|
514 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
515 |
total_cooling_load += cooling_load
|
516 |
total_heating_load += heating_load
|
517 |
-
|
|
|
|
|
|
|
|
|
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 =
|
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 |
-
|
556 |
-
|
|
|
|
|
557 |
elif system_type == "Effective Leakage Area":
|
558 |
-
ela = system.get("effective_air_leakage_area", 100.0) / 10000 #
|
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
|
566 |
-
|
567 |
elif system_type == "Flow Coefficient":
|
568 |
-
c = system.get("flow_coefficient", 0.0001)
|
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 |
-
|
580 |
-
|
581 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
582 |
total_cooling_load += cooling_load
|
583 |
total_heating_load += heating_load
|
584 |
-
|
|
|
|
|
|
|
|
|
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 |
-
"
|
1398 |
-
"
|
1399 |
-
"
|
1400 |
-
"
|
1401 |
-
"
|
|
|
|
|
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"]["
|
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
|