Spaces:
Sleeping
Sleeping
Update app/hvac_loads.py
Browse files- app/hvac_loads.py +206 -89
app/hvac_loads.py
CHANGED
@@ -19,6 +19,7 @@ import math
|
|
19 |
from utils.ctf_calculations import CTFCalculator, ComponentType, CTFCoefficients
|
20 |
from utils.solar import SolarCalculations # Import SolarCalculations for SHGC data
|
21 |
from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, DEFAULT_MATERIAL_PROPERTIES, DEFAULT_WINDOW_PROPERTIES
|
|
|
22 |
|
23 |
# Configure logging
|
24 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
@@ -68,6 +69,27 @@ class AdaptiveComfortModel:
|
|
68 |
setpoints[key] = (t_min + t_max) / 2
|
69 |
return setpoints
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
class TFMCalculations:
|
72 |
@staticmethod
|
73 |
def get_component_type(component: Dict[str, Any]) -> ComponentType:
|
@@ -689,6 +711,8 @@ class TFMCalculations:
|
|
689 |
indoor_cond = TFMCalculations.get_indoor_conditions(indoor_conditions, hour, outdoor_temp, month, day, adaptive_setpoints)
|
690 |
indoor_temp = indoor_cond["temperature"]
|
691 |
conduction_cooling = conduction_heating = solar = internal = ventilation_cooling = ventilation_heating = infiltration_cooling = infiltration_heating = 0
|
|
|
|
|
692 |
is_operating = False
|
693 |
for period in operating_periods:
|
694 |
start_hour = period.get("start", 8)
|
@@ -701,8 +725,13 @@ class TFMCalculations:
|
|
701 |
for comp_list in components.values():
|
702 |
for comp in comp_list:
|
703 |
cool_load, _ = TFMCalculations.calculate_conduction_load(comp, outdoor_temp, indoor_temp, hour, mode="cooling")
|
704 |
-
conduction_cooling += cool_load
|
705 |
component_solar_load = TFMCalculations.calculate_solar_load(comp, hour_data, hour, building_orientation, mode="cooling")
|
|
|
|
|
|
|
|
|
|
|
|
|
706 |
solar += component_solar_load
|
707 |
logger.info(f"Component {comp.get('name', 'Unknown')} ({TFMCalculations.get_component_type(comp).value}) solar load: {component_solar_load:.3f} kW, accumulated solar: {solar:.3f} kW")
|
708 |
|
@@ -713,6 +742,10 @@ class TFMCalculations:
|
|
713 |
for comp_list in components.values():
|
714 |
for comp in comp_list:
|
715 |
_, heat_load = TFMCalculations.calculate_conduction_load(comp, outdoor_temp, indoor_temp, hour, mode="heating")
|
|
|
|
|
|
|
|
|
716 |
conduction_heating += heat_load
|
717 |
internal = TFMCalculations.calculate_internal_load(internal_loads, hour, max([p["end"] - p["start"] for p in operating_periods]), area)
|
718 |
_, ventilation_heating = TFMCalculations.calculate_ventilation_load(internal_loads, outdoor_temp, indoor_temp, area, building_info, hour, mode="heating")
|
@@ -742,7 +775,9 @@ class TFMCalculations:
|
|
742 |
"infiltration_heating": infiltration_heating,
|
743 |
"total_cooling": total_cooling,
|
744 |
"total_heating": total_heating,
|
745 |
-
"ground_temperature": ground_temp
|
|
|
|
|
746 |
})
|
747 |
loads_by_day = defaultdict(list)
|
748 |
for load in temp_loads:
|
@@ -763,6 +798,116 @@ class TFMCalculations:
|
|
763 |
final_loads.append(load)
|
764 |
return final_loads
|
765 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
766 |
def display_hvac_loads_page():
|
767 |
"""
|
768 |
Display the HVAC Loads page in the Streamlit application.
|
@@ -966,93 +1111,65 @@ def display_hvac_loads_page():
|
|
966 |
# Calculate HVAC Loads
|
967 |
if st.button("Calculate HVAC Loads"):
|
968 |
try:
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
-
|
997 |
-
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
|
1009 |
-
|
1010 |
-
|
1011 |
-
|
1012 |
-
|
1013 |
-
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
st.write(f"{key.capitalize()}: {value:.2f} kW")
|
1029 |
-
st.subheader("Heating Load Breakdown")
|
1030 |
-
for key, value in heating_breakdown.items():
|
1031 |
-
st.write(f"{key.capitalize()}: {value:.2f} kW")
|
1032 |
-
|
1033 |
-
# Optional: Display Hourly Loads Table in Debug Mode
|
1034 |
-
if st.session_state.debug_mode:
|
1035 |
-
st.subheader("Hourly Loads (Debug Mode)")
|
1036 |
-
hourly_df = []
|
1037 |
-
for load in loads:
|
1038 |
-
hourly_df.append({
|
1039 |
-
"Month": load["month"],
|
1040 |
-
"Day": load["day"],
|
1041 |
-
"Hour": load["hour"],
|
1042 |
-
"Total Cooling (kW)": load["total_cooling"],
|
1043 |
-
"Total Heating (kW)": load["total_heating"],
|
1044 |
-
"Conduction Cooling (kW)": load["conduction_cooling"],
|
1045 |
-
"Solar (kW)": load["solar"],
|
1046 |
-
"Internal (kW)": load["internal"],
|
1047 |
-
"Ventilation Cooling (kW)": load["ventilation_cooling"],
|
1048 |
-
"Infiltration Cooling (kW)": load["infiltration_cooling"],
|
1049 |
-
"Ventilation Heating (kW)": load["ventilation_heating"],
|
1050 |
-
"Infiltration Heating (kW)": load["infiltration_heating"]
|
1051 |
-
})
|
1052 |
-
st.dataframe(hourly_df)
|
1053 |
-
|
1054 |
-
st.success("HVAC loads calculated successfully.")
|
1055 |
-
logger.info("HVAC loads calculated and stored in session state")
|
1056 |
|
1057 |
except Exception as e:
|
1058 |
st.error(f"Error calculating HVAC loads: {str(e)}")
|
|
|
19 |
from utils.ctf_calculations import CTFCalculator, ComponentType, CTFCoefficients
|
20 |
from utils.solar import SolarCalculations # Import SolarCalculations for SHGC data
|
21 |
from app.m_c_data import SAMPLE_MATERIALS, SAMPLE_FENESTRATIONS, DEFAULT_MATERIAL_PROPERTIES, DEFAULT_WINDOW_PROPERTIES
|
22 |
+
import plotly.express as px
|
23 |
|
24 |
# Configure logging
|
25 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
69 |
setpoints[key] = (t_min + t_max) / 2
|
70 |
return setpoints
|
71 |
|
72 |
+
def classify_azimuth(azimuth: float) -> str:
|
73 |
+
"""Classify azimuth angle into cardinal directions."""
|
74 |
+
azimuth = azimuth % 360
|
75 |
+
if 337.5 <= azimuth or azimuth < 22.5:
|
76 |
+
return "N"
|
77 |
+
elif 22.5 <= azimuth < 67.5:
|
78 |
+
return "NE"
|
79 |
+
elif 67.5 <= azimuth < 112.5:
|
80 |
+
return "E"
|
81 |
+
elif 112.5 <= azimuth < 157.5:
|
82 |
+
return "SE"
|
83 |
+
elif 157.5 <= azimuth < 202.5:
|
84 |
+
return "S"
|
85 |
+
elif 202.5 <= azimuth < 247.5:
|
86 |
+
return "SW"
|
87 |
+
elif 247.5 <= azimuth < 292.5:
|
88 |
+
return "W"
|
89 |
+
elif 292.5 <= azimuth < 337.5:
|
90 |
+
return "NW"
|
91 |
+
return "N"
|
92 |
+
|
93 |
class TFMCalculations:
|
94 |
@staticmethod
|
95 |
def get_component_type(component: Dict[str, Any]) -> ComponentType:
|
|
|
711 |
indoor_cond = TFMCalculations.get_indoor_conditions(indoor_conditions, hour, outdoor_temp, month, day, adaptive_setpoints)
|
712 |
indoor_temp = indoor_cond["temperature"]
|
713 |
conduction_cooling = conduction_heating = solar = internal = ventilation_cooling = ventilation_heating = infiltration_cooling = infiltration_heating = 0
|
714 |
+
solar_by_orientation = defaultdict(float)
|
715 |
+
conduction_by_orientation = defaultdict(float)
|
716 |
is_operating = False
|
717 |
for period in operating_periods:
|
718 |
start_hour = period.get("start", 8)
|
|
|
725 |
for comp_list in components.values():
|
726 |
for comp in comp_list:
|
727 |
cool_load, _ = TFMCalculations.calculate_conduction_load(comp, outdoor_temp, indoor_temp, hour, mode="cooling")
|
|
|
728 |
component_solar_load = TFMCalculations.calculate_solar_load(comp, hour_data, hour, building_orientation, mode="cooling")
|
729 |
+
orientation = classify_azimuth(comp.get("surface_azimuth", 0))
|
730 |
+
element = TFMCalculations.get_component_type(comp).name
|
731 |
+
key = orientation if element in ["WALL", "WINDOW"] else element
|
732 |
+
conduction_by_orientation[key] += cool_load
|
733 |
+
solar_by_orientation[key] += component_solar_load
|
734 |
+
conduction_cooling += cool_load
|
735 |
solar += component_solar_load
|
736 |
logger.info(f"Component {comp.get('name', 'Unknown')} ({TFMCalculations.get_component_type(comp).value}) solar load: {component_solar_load:.3f} kW, accumulated solar: {solar:.3f} kW")
|
737 |
|
|
|
742 |
for comp_list in components.values():
|
743 |
for comp in comp_list:
|
744 |
_, heat_load = TFMCalculations.calculate_conduction_load(comp, outdoor_temp, indoor_temp, hour, mode="heating")
|
745 |
+
orientation = classify_azimuth(comp.get("surface_azimuth", 0))
|
746 |
+
element = TFMCalculations.get_component_type(comp).name
|
747 |
+
key = orientation if element in ["WALL", "WINDOW"] else element
|
748 |
+
conduction_by_orientation[key] += heat_load
|
749 |
conduction_heating += heat_load
|
750 |
internal = TFMCalculations.calculate_internal_load(internal_loads, hour, max([p["end"] - p["start"] for p in operating_periods]), area)
|
751 |
_, ventilation_heating = TFMCalculations.calculate_ventilation_load(internal_loads, outdoor_temp, indoor_temp, area, building_info, hour, mode="heating")
|
|
|
775 |
"infiltration_heating": infiltration_heating,
|
776 |
"total_cooling": total_cooling,
|
777 |
"total_heating": total_heating,
|
778 |
+
"ground_temperature": ground_temp,
|
779 |
+
"solar_by_orientation": dict(solar_by_orientation),
|
780 |
+
"conduction_by_orientation": dict(conduction_by_orientation)
|
781 |
})
|
782 |
loads_by_day = defaultdict(list)
|
783 |
for load in temp_loads:
|
|
|
798 |
final_loads.append(load)
|
799 |
return final_loads
|
800 |
|
801 |
+
def make_pie(data: Dict[str, float], title: str) -> px.pie:
|
802 |
+
"""Create a Plotly pie chart from a dictionary of values."""
|
803 |
+
fig = px.pie(
|
804 |
+
names=list(data.keys()),
|
805 |
+
values=list(data.values()),
|
806 |
+
title=title,
|
807 |
+
hole=0.4
|
808 |
+
)
|
809 |
+
fig.update_traces(textinfo='percent+label', hoverinfo='label+percent+value')
|
810 |
+
return fig
|
811 |
+
|
812 |
+
def display_hvac_results_ui(loads: List[Dict[str, Any]]):
|
813 |
+
"""Display HVAC load results with enhanced UI elements."""
|
814 |
+
st.subheader("HVAC Load Results")
|
815 |
+
|
816 |
+
# Monthly Load Graph
|
817 |
+
st.subheader("Monthly Heating and Cooling Load")
|
818 |
+
monthly_df = pd.DataFrame(loads).groupby("month").agg({
|
819 |
+
"total_cooling": "sum",
|
820 |
+
"total_heating": "sum"
|
821 |
+
}).reset_index()
|
822 |
+
monthly_df = monthly_df.rename(columns={"total_cooling": "Cooling", "total_heating": "Heating"})
|
823 |
+
fig = px.line(monthly_df, x="month", y=["Cooling", "Heating"], markers=True, title="Monthly Load Summary")
|
824 |
+
fig.update_xaxes(title="Month", tickvals=list(range(1, 13)))
|
825 |
+
fig.update_yaxes(title="Load (kW)")
|
826 |
+
st.plotly_chart(fig)
|
827 |
+
st.session_state.project_data["hvac_loads"]["monthly_summary"] = monthly_df.to_dict()
|
828 |
+
|
829 |
+
# Two-Column Layout for Equipment Sizing and Pie Charts
|
830 |
+
col1, col2 = st.columns(2)
|
831 |
+
with col1:
|
832 |
+
st.subheader("Equipment Sizing")
|
833 |
+
cooling_loads = [load for load in loads if load["total_cooling"] > 0]
|
834 |
+
heating_loads = [load for load in loads if load["total_heating"] > 0]
|
835 |
+
peak_cooling = max(cooling_loads, key=lambda x: x["total_cooling"]) if cooling_loads else None
|
836 |
+
peak_heating = max(heating_loads, key=lambda x: x["total_heating"]) if heating_loads else None
|
837 |
+
if peak_cooling:
|
838 |
+
st.write(f"**Peak Cooling Load**: {peak_cooling['total_cooling']:.2f} kW")
|
839 |
+
st.write(f"Occurred on: {peak_cooling['month']}/{peak_cooling['day']} at {peak_cooling['hour']}:00")
|
840 |
+
else:
|
841 |
+
st.write("**Peak Cooling Load**: 0.00 kW")
|
842 |
+
if peak_heating:
|
843 |
+
st.write(f"**Peak Heating Load**: {peak_heating['total_heating']:.2f} kW")
|
844 |
+
st.write(f"Occurred on: {peak_heating['month']}/{peak_heating['day']} at {peak_heating['hour']}:00")
|
845 |
+
else:
|
846 |
+
st.write("**Peak Heating Load**: 0.00 kW")
|
847 |
+
|
848 |
+
with col2:
|
849 |
+
st.subheader("Cooling Load Breakdown")
|
850 |
+
cooling_breakdown = {
|
851 |
+
"Conduction": sum(load["conduction_cooling"] for load in cooling_loads),
|
852 |
+
"Solar Gains": sum(load["solar"] for load in cooling_loads),
|
853 |
+
"Internal": sum(load["internal"] for load in cooling_loads),
|
854 |
+
"Ventilation": sum(load["ventilation_cooling"] for load in cooling_loads),
|
855 |
+
"Infiltration": sum(load["infiltration_cooling"] for load in cooling_loads)
|
856 |
+
}
|
857 |
+
st.markdown("**Conduction**", help="Heat transfer through building envelope materials.")
|
858 |
+
st.markdown("**Solar Gains**", help="Radiative heat gains from sun through surfaces and windows.")
|
859 |
+
st.markdown("**Ventilation**", help="Loads due to outdoor air entering through ventilation systems.")
|
860 |
+
st.markdown("**Infiltration**", help="Loads due to unintended air leakage through the building envelope.")
|
861 |
+
st.markdown("**Internal**", help="Heat gains from occupants, lighting, and equipment inside the building.")
|
862 |
+
cooling_pie = make_pie({k: v for k, v in cooling_breakdown.items() if v > 0}, "Cooling Load Components")
|
863 |
+
st.plotly_chart(cooling_pie)
|
864 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_component"] = cooling_breakdown
|
865 |
+
|
866 |
+
# Second Row for Heating and Orientation Breakdown
|
867 |
+
col3, col4 = st.columns(2)
|
868 |
+
with col3:
|
869 |
+
st.subheader("Heating Load Breakdown")
|
870 |
+
heating_breakdown = {
|
871 |
+
"Conduction": sum(load["conduction_heating"] for load in heating_loads),
|
872 |
+
"Ventilation": sum(load["ventilation_heating"] for load in heating_loads),
|
873 |
+
"Infiltration": sum(load["infiltration_heating"] for load in heating_loads)
|
874 |
+
}
|
875 |
+
heating_pie = make_pie({k: v for k, v in heating_breakdown.items() if v > 0}, "Heating Load Components")
|
876 |
+
st.plotly_chart(heating_pie)
|
877 |
+
st.session_state.project_data["hvac_loads"]["heating"]["charts"]["pie_by_component"] = heating_breakdown
|
878 |
+
|
879 |
+
with col4:
|
880 |
+
st.subheader("Cooling Load by Orientation")
|
881 |
+
orientation_solar = defaultdict(float)
|
882 |
+
orientation_conduction = defaultdict(float)
|
883 |
+
for load in cooling_loads:
|
884 |
+
for key, value in load["solar_by_orientation"].items():
|
885 |
+
orientation_solar[key] += value
|
886 |
+
for key, value in load["conduction_by_orientation"].items():
|
887 |
+
orientation_conduction[key] += value
|
888 |
+
orientation_breakdown = {k: orientation_solar[k] + orientation_conduction[k] for k in set(orientation_solar) | set(orientation_conduction)}
|
889 |
+
orientation_pie = make_pie({k: v for k, v in orientation_breakdown.items() if v > 0}, "Cooling Load by Orientation/Component")
|
890 |
+
st.plotly_chart(orientation_pie)
|
891 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_orientation"] = orientation_breakdown
|
892 |
+
|
893 |
+
# Interactive Filtering
|
894 |
+
st.subheader("Explore Hourly Loads")
|
895 |
+
df = pd.DataFrame(loads)
|
896 |
+
col5, col6 = st.columns(2)
|
897 |
+
with col5:
|
898 |
+
selected_month = st.selectbox("Select Month", sorted(df["month"].unique()), key="filter_month")
|
899 |
+
with col6:
|
900 |
+
selected_day = st.selectbox("Select Day", sorted(df[df["month"] == selected_month]["day"].unique()), key="filter_day")
|
901 |
+
filtered_df = df[(df["month"] == selected_month) & (df["day"] == selected_day)]
|
902 |
+
st.dataframe(filtered_df[[
|
903 |
+
"hour", "total_cooling", "total_heating", "conduction_cooling", "solar", "internal",
|
904 |
+
"ventilation_cooling", "infiltration_cooling", "ventilation_heating", "infiltration_heating"
|
905 |
+
]])
|
906 |
+
|
907 |
+
# CSV Export
|
908 |
+
csv = filtered_df.to_csv(index=False)
|
909 |
+
st.download_button("Download Hourly Summary as CSV", data=csv, file_name=f"hourly_loads_{selected_month}_{selected_day}.csv")
|
910 |
+
|
911 |
def display_hvac_loads_page():
|
912 |
"""
|
913 |
Display the HVAC Loads page in the Streamlit application.
|
|
|
1111 |
# Calculate HVAC Loads
|
1112 |
if st.button("Calculate HVAC Loads"):
|
1113 |
try:
|
1114 |
+
with st.spinner("Running simulation... this may take up to a minute depending on data size."):
|
1115 |
+
components = st.session_state.project_data["components"]
|
1116 |
+
hourly_data = st.session_state.project_data["climate_data"]["hourly_data"]
|
1117 |
+
indoor_conditions = st.session_state.project_data["indoor_conditions"]
|
1118 |
+
internal_loads = st.session_state.project_data["internal_loads"]
|
1119 |
+
building_info = st.session_state.project_data["building_info"]
|
1120 |
+
sim_period = st.session_state.project_data["sim_period"]
|
1121 |
+
hvac_settings = st.session_state.project_data["hvac_settings"]
|
1122 |
+
|
1123 |
+
if not hourly_data:
|
1124 |
+
st.error("No climate data available. Please configure climate data first.")
|
1125 |
+
logger.error("HVAC calculation failed: No climate data available")
|
1126 |
+
return
|
1127 |
+
elif not any(comp_list for comp_list in components.values()):
|
1128 |
+
st.error("No building components defined. Please configure components first.")
|
1129 |
+
logger.error("HVAC calculation failed: No building components defined")
|
1130 |
+
return
|
1131 |
+
else:
|
1132 |
+
loads = TFMCalculations.calculate_tfm_loads(
|
1133 |
+
components=components,
|
1134 |
+
hourly_data=hourly_data,
|
1135 |
+
indoor_conditions=indoor_conditions,
|
1136 |
+
internal_loads=internal_loads,
|
1137 |
+
building_info=building_info,
|
1138 |
+
sim_period=sim_period,
|
1139 |
+
hvac_settings=hvac_settings
|
1140 |
+
)
|
1141 |
+
|
1142 |
+
# Update session state with results
|
1143 |
+
cooling_loads = [load for load in loads if load["total_cooling"] > 0]
|
1144 |
+
heating_loads = [load for load in loads if load["total_heating"] > 0]
|
1145 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["hourly"] = cooling_loads
|
1146 |
+
st.session_state.project_data["hvac_loads"]["heating"]["hourly"] = heating_loads
|
1147 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["peak"] = max([load["total_cooling"] for load in cooling_loads], default=0)
|
1148 |
+
st.session_state.project_data["hvac_loads"]["heating"]["peak"] = max([load["total_heating"] for load in heating_loads], default=0)
|
1149 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["charts"] = {}
|
1150 |
+
st.session_state.project_data["hvac_loads"]["heating"]["charts"] = {}
|
1151 |
+
|
1152 |
+
# Store breakdown
|
1153 |
+
cooling_breakdown = {
|
1154 |
+
"conduction": sum(load["conduction_cooling"] for load in cooling_loads),
|
1155 |
+
"solar": sum(load["solar"] for load in cooling_loads if load["total_cooling"] > 0),
|
1156 |
+
"internal": sum(load["internal"] for load in cooling_loads if load["total_cooling"] > 0),
|
1157 |
+
"ventilation": sum(load["ventilation_cooling"] for load in cooling_loads),
|
1158 |
+
"infiltration": sum(load["infiltration_cooling"] for load in cooling_loads)
|
1159 |
+
}
|
1160 |
+
heating_breakdown = {
|
1161 |
+
"conduction": sum(load["conduction_heating"] for load in heating_loads),
|
1162 |
+
"ventilation": sum(load["ventilation_heating"] for load in heating_loads),
|
1163 |
+
"infiltration": sum(load["infiltration_heating"] for load in heating_loads)
|
1164 |
+
}
|
1165 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["breakdown"] = cooling_breakdown
|
1166 |
+
st.session_state.project_data["hvac_loads"]["heating"]["breakdown"] = heating_breakdown
|
1167 |
+
|
1168 |
+
# Display Results UI
|
1169 |
+
display_hvac_results_ui(loads)
|
1170 |
+
|
1171 |
+
st.success("HVAC loads calculated successfully.")
|
1172 |
+
logger.info("HVAC loads calculated and stored in session state")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1173 |
|
1174 |
except Exception as e:
|
1175 |
st.error(f"Error calculating HVAC loads: {str(e)}")
|