Spaces:
Sleeping
Sleeping
Update app/hvac_loads.py
Browse files- app/hvac_loads.py +93 -145
app/hvac_loads.py
CHANGED
|
@@ -801,15 +801,7 @@ class TFMCalculations:
|
|
| 801 |
return final_loads
|
| 802 |
|
| 803 |
def make_pie(data: Dict[str, float], title: str) -> px.pie:
|
| 804 |
-
"""Create a Plotly pie chart from a dictionary of values.
|
| 805 |
-
|
| 806 |
-
Args:
|
| 807 |
-
data: Dictionary with keys as labels and values as magnitudes.
|
| 808 |
-
title: Title for the pie chart.
|
| 809 |
-
|
| 810 |
-
Returns:
|
| 811 |
-
Plotly pie chart figure.
|
| 812 |
-
"""
|
| 813 |
fig = px.pie(
|
| 814 |
names=list(data.keys()),
|
| 815 |
values=list(data.values()),
|
|
@@ -819,135 +811,41 @@ def make_pie(data: Dict[str, float], title: str) -> px.pie:
|
|
| 819 |
fig.update_traces(textinfo='percent+label', hoverinfo='label+percent+value')
|
| 820 |
return fig
|
| 821 |
|
| 822 |
-
@st.cache_data
|
| 823 |
-
def compute_chart_data(_loads: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 824 |
-
"""Compute and cache chart and breakdown data to avoid recalculation.
|
| 825 |
-
|
| 826 |
-
Args:
|
| 827 |
-
_loads: List of dictionaries containing HVAC load data.
|
| 828 |
-
|
| 829 |
-
Returns:
|
| 830 |
-
Dictionary containing precomputed data for charts and breakdowns.
|
| 831 |
-
"""
|
| 832 |
-
cooling_loads = [load for load in _loads if load["total_cooling"] > 0]
|
| 833 |
-
heating_loads = [load for load in _loads if load["total_heating"] > 0]
|
| 834 |
-
|
| 835 |
-
# Monthly summary
|
| 836 |
-
monthly_df = pd.DataFrame(_loads).groupby("month").agg({
|
| 837 |
-
"total_cooling": "sum",
|
| 838 |
-
"total_heating": "sum"
|
| 839 |
-
}).reset_index()
|
| 840 |
-
monthly_df = monthly_df.rename(columns={"total_cooling": "Cooling", "total_heating": "Heating"})
|
| 841 |
-
|
| 842 |
-
# Cooling breakdown
|
| 843 |
-
cooling_breakdown = {
|
| 844 |
-
"Conduction": sum(load["conduction_cooling"] for load in cooling_loads),
|
| 845 |
-
"Solar Gains": sum(load["solar"] for load in cooling_loads),
|
| 846 |
-
"Internal": sum(load["internal"] for load in cooling_loads),
|
| 847 |
-
"Ventilation": sum(load["ventilation_cooling"] for load in cooling_loads),
|
| 848 |
-
"Infiltration": sum(load["infiltration_cooling"] for load in cooling_loads)
|
| 849 |
-
}
|
| 850 |
-
|
| 851 |
-
# Heating breakdown
|
| 852 |
-
heating_breakdown = {
|
| 853 |
-
"Conduction": sum(load["conduction_heating"] for load in heating_loads),
|
| 854 |
-
"Ventilation": sum(load["ventilation_heating"] for load in heating_loads),
|
| 855 |
-
"Infiltration": sum(load["infiltration_heating"] for load in heating_loads)
|
| 856 |
-
}
|
| 857 |
-
|
| 858 |
-
# Orientation breakdowns
|
| 859 |
-
orientation_solar = defaultdict(float)
|
| 860 |
-
orientation_conduction_cooling = defaultdict(float)
|
| 861 |
-
orientation_conduction_heating = defaultdict(float)
|
| 862 |
-
for load in cooling_loads:
|
| 863 |
-
for key, value in load["solar_by_orientation"].items():
|
| 864 |
-
orientation_solar[key] += value
|
| 865 |
-
for key, value in load["conduction_by_orientation"].items():
|
| 866 |
-
orientation_conduction_cooling[key] += value
|
| 867 |
-
for load in heating_loads:
|
| 868 |
-
for key, value in load["conduction_by_orientation"].items():
|
| 869 |
-
orientation_conduction_heating[key] += value
|
| 870 |
-
cooling_orientation_breakdown = {k: orientation_solar[k] + orientation_conduction_cooling[k] for k in set(orientation_solar) | set(orientation_conduction_cooling)}
|
| 871 |
-
heating_orientation_breakdown = {k: v for k, v in orientation_conduction_heating.items() if v > 0}
|
| 872 |
-
|
| 873 |
-
# Peak loads
|
| 874 |
-
peak_cooling = max(cooling_loads, key=lambda x: x["total_cooling"]) if cooling_loads else None
|
| 875 |
-
peak_heating = max(heating_loads, key=lambda x: x["total_heating"]) if heating_loads else None
|
| 876 |
-
|
| 877 |
-
return {
|
| 878 |
-
"monthly_df": monthly_df,
|
| 879 |
-
"cooling_breakdown": cooling_breakdown,
|
| 880 |
-
"heating_breakdown": heating_breakdown,
|
| 881 |
-
"cooling_orientation_breakdown": cooling_orientation_breakdown,
|
| 882 |
-
"heating_orientation_breakdown": heating_orientation_breakdown,
|
| 883 |
-
"peak_cooling": peak_cooling,
|
| 884 |
-
"peak_heating": peak_heating
|
| 885 |
-
}
|
| 886 |
-
|
| 887 |
-
@st.cache_data
|
| 888 |
-
def compute_table_data(_loads: List[Dict[str, Any]]) -> pd.DataFrame:
|
| 889 |
-
"""Compute and cache DataFrame for hourly loads table.
|
| 890 |
-
|
| 891 |
-
Args:
|
| 892 |
-
_loads: List of dictionaries containing HVAC load data.
|
| 893 |
-
|
| 894 |
-
Returns:
|
| 895 |
-
Pandas DataFrame with hourly load data and orientation columns.
|
| 896 |
-
"""
|
| 897 |
-
df = pd.DataFrame(_loads)
|
| 898 |
-
unique_orientations = set()
|
| 899 |
-
for load in _loads:
|
| 900 |
-
unique_orientations.update(load["solar_by_orientation"].keys())
|
| 901 |
-
unique_orientations.update(load["conduction_by_orientation"].keys())
|
| 902 |
-
|
| 903 |
-
columns = [
|
| 904 |
-
"month", "day", "hour",
|
| 905 |
-
"total_cooling", "total_heating",
|
| 906 |
-
"conduction_cooling", "solar", "internal",
|
| 907 |
-
"ventilation_cooling", "infiltration_cooling",
|
| 908 |
-
"ventilation_heating", "infiltration_heating"
|
| 909 |
-
]
|
| 910 |
-
for orient in sorted(unique_orientations):
|
| 911 |
-
df[f"solar_{orient}"] = df["solar_by_orientation"].apply(lambda x: x.get(orient, 0.0))
|
| 912 |
-
df[f"conduction_{orient}"] = df["conduction_by_orientation"].apply(lambda x: x.get(orient, 0.0))
|
| 913 |
-
columns.extend([f"solar_{orient}", f"conduction_{orient}"])
|
| 914 |
-
|
| 915 |
-
return df[columns]
|
| 916 |
-
|
| 917 |
def display_hvac_results_ui(loads: List[Dict[str, Any]], run_id: str = "default"):
|
| 918 |
-
"""Display HVAC load results with enhanced UI elements in a two-column format.
|
| 919 |
-
|
| 920 |
-
Args:
|
| 921 |
-
loads: List of dictionaries containing HVAC load data.
|
| 922 |
-
run_id: Unique identifier for the simulation run to ensure unique widget keys.
|
| 923 |
-
"""
|
| 924 |
st.subheader("HVAC Load Results")
|
| 925 |
|
| 926 |
-
# Compute cached data
|
| 927 |
-
chart_data = compute_chart_data(loads)
|
| 928 |
-
|
| 929 |
# First Row: Equipment Sizing (Two Columns)
|
| 930 |
col1, col2 = st.columns(2)
|
| 931 |
with col1:
|
| 932 |
st.subheader("Cooling Equipment Sizing")
|
| 933 |
-
if
|
| 934 |
-
|
| 935 |
-
|
|
|
|
|
|
|
| 936 |
else:
|
| 937 |
st.write("**Peak Cooling Load**: 0.00 kW")
|
| 938 |
|
| 939 |
with col2:
|
| 940 |
st.subheader("Heating Equipment Sizing")
|
| 941 |
-
if
|
| 942 |
-
|
| 943 |
-
|
|
|
|
|
|
|
| 944 |
else:
|
| 945 |
st.write("**Peak Heating Load**: 0.00 kW")
|
| 946 |
|
| 947 |
# Second Row: Monthly Loads Graph (Single Column)
|
| 948 |
st.subheader("Monthly Heating and Cooling Load")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 949 |
fig = px.bar(
|
| 950 |
-
|
| 951 |
x="month",
|
| 952 |
y=["Cooling", "Heating"],
|
| 953 |
barmode="group",
|
|
@@ -956,50 +854,87 @@ def display_hvac_results_ui(loads: List[Dict[str, Any]], run_id: str = "default"
|
|
| 956 |
fig.update_xaxes(title="Month", tickvals=list(range(1, 13)))
|
| 957 |
fig.update_yaxes(title="Load (kW)")
|
| 958 |
st.plotly_chart(fig, use_container_width=True)
|
| 959 |
-
st.session_state.project_data["hvac_loads"]["monthly_summary"] =
|
| 960 |
|
| 961 |
# Third Row: Load Breakdown (Two Columns)
|
| 962 |
col3, col4 = st.columns(2)
|
| 963 |
with col3:
|
| 964 |
st.subheader("Cooling Load Breakdown")
|
| 965 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 966 |
st.plotly_chart(cooling_pie)
|
| 967 |
-
st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_component"] =
|
| 968 |
|
| 969 |
with col4:
|
| 970 |
st.subheader("Heating Load Breakdown")
|
| 971 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 972 |
st.plotly_chart(heating_pie)
|
| 973 |
-
st.session_state.project_data["hvac_loads"]["heating"]["charts"]["pie_by_component"] =
|
| 974 |
|
| 975 |
# Fourth Row: Heat Gain by Orientation (Two Columns)
|
| 976 |
col5, col6 = st.columns(2)
|
| 977 |
with col5:
|
| 978 |
st.subheader("Cooling Heat Gain by Orientation")
|
| 979 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 980 |
st.plotly_chart(orientation_pie)
|
| 981 |
-
st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_orientation"] =
|
| 982 |
|
| 983 |
with col6:
|
| 984 |
st.subheader("Heating Heat Gain by Orientation")
|
| 985 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 986 |
st.plotly_chart(orientation_pie)
|
| 987 |
-
st.session_state.project_data["hvac_loads"]["heating"]["charts"]["pie_by_orientation"] =
|
| 988 |
|
| 989 |
# Fifth Row: Explore Hourly Loads (Single Column)
|
| 990 |
st.subheader("Explore Hourly Loads")
|
| 991 |
-
|
| 992 |
-
#
|
| 993 |
-
|
|
|
|
|
|
|
|
|
|
| 994 |
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
-
|
| 1001 |
-
|
| 1002 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1003 |
|
| 1004 |
def display_hvac_loads_page():
|
| 1005 |
"""
|
|
@@ -1011,6 +946,7 @@ def display_hvac_loads_page():
|
|
| 1011 |
st.markdown("Configure and calculate HVAC loads for the building.")
|
| 1012 |
|
| 1013 |
# Generate a unique run ID for this session
|
|
|
|
| 1014 |
run_id = str(uuid.uuid4())
|
| 1015 |
|
| 1016 |
# Check for existing results
|
|
@@ -1021,6 +957,7 @@ def display_hvac_loads_page():
|
|
| 1021 |
heating_loads = st.session_state.project_data["hvac_loads"]["heating"].get("hourly", [])
|
| 1022 |
loads.extend(cooling_loads)
|
| 1023 |
loads.extend(heating_loads)
|
|
|
|
| 1024 |
loads = sorted(loads, key=lambda x: (x["month"], x["day"], x["hour"]))
|
| 1025 |
if loads:
|
| 1026 |
st.info("Displaying previously calculated HVAC load results.")
|
|
@@ -1036,7 +973,7 @@ def display_hvac_loads_page():
|
|
| 1036 |
"Latitude": climate_data.get("location", {}).get("latitude", 0.0),
|
| 1037 |
"Longitude": climate_data.get("location", {}).get("longitude", 0.0),
|
| 1038 |
"Elevation": climate_data.get("location", {}).get("elevation", 0.0),
|
| 1039 |
-
"Time Zone": climate_data.get("location", {}).get("timezone",
|
| 1040 |
"Ground Reflectivity": climate_data.get("ground_reflectivity", 0.2)
|
| 1041 |
}
|
| 1042 |
|
|
@@ -1263,10 +1200,21 @@ def display_hvac_loads_page():
|
|
| 1263 |
st.session_state.project_data["hvac_loads"]["cooling"]["charts"] = {}
|
| 1264 |
st.session_state.project_data["hvac_loads"]["heating"]["charts"] = {}
|
| 1265 |
|
| 1266 |
-
# Store breakdown
|
| 1267 |
-
|
| 1268 |
-
|
| 1269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1270 |
|
| 1271 |
# Display Results UI with unique run_id
|
| 1272 |
display_hvac_results_ui(loads, run_id=run_id)
|
|
|
|
| 801 |
return final_loads
|
| 802 |
|
| 803 |
def make_pie(data: Dict[str, float], title: str) -> px.pie:
|
| 804 |
+
"""Create a Plotly pie chart from a dictionary of values."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 805 |
fig = px.pie(
|
| 806 |
names=list(data.keys()),
|
| 807 |
values=list(data.values()),
|
|
|
|
| 811 |
fig.update_traces(textinfo='percent+label', hoverinfo='label+percent+value')
|
| 812 |
return fig
|
| 813 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 814 |
def display_hvac_results_ui(loads: List[Dict[str, Any]], run_id: str = "default"):
|
| 815 |
+
"""Display HVAC load results with enhanced UI elements in a two-column format."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 816 |
st.subheader("HVAC Load Results")
|
| 817 |
|
|
|
|
|
|
|
|
|
|
| 818 |
# First Row: Equipment Sizing (Two Columns)
|
| 819 |
col1, col2 = st.columns(2)
|
| 820 |
with col1:
|
| 821 |
st.subheader("Cooling Equipment Sizing")
|
| 822 |
+
cooling_loads = [load for load in loads if load["total_cooling"] > 0]
|
| 823 |
+
peak_cooling = max(cooling_loads, key=lambda x: x["total_cooling"]) if cooling_loads else None
|
| 824 |
+
if peak_cooling:
|
| 825 |
+
st.write(f"**Peak Cooling Load**: {peak_cooling['total_cooling']:.2f} kW")
|
| 826 |
+
st.write(f"Occurred on: {peak_cooling['month']}/{peak_cooling['day']} at {peak_cooling['hour']}:00")
|
| 827 |
else:
|
| 828 |
st.write("**Peak Cooling Load**: 0.00 kW")
|
| 829 |
|
| 830 |
with col2:
|
| 831 |
st.subheader("Heating Equipment Sizing")
|
| 832 |
+
heating_loads = [load for load in loads if load["total_heating"] > 0]
|
| 833 |
+
peak_heating = max(heating_loads, key=lambda x: x["total_heating"]) if heating_loads else None
|
| 834 |
+
if peak_heating:
|
| 835 |
+
st.write(f"**Peak Heating Load**: {peak_heating['total_heating']:.2f} kW")
|
| 836 |
+
st.write(f"Occurred on: {peak_heating['month']}/{peak_heating['day']} at {peak_heating['hour']}:00")
|
| 837 |
else:
|
| 838 |
st.write("**Peak Heating Load**: 0.00 kW")
|
| 839 |
|
| 840 |
# Second Row: Monthly Loads Graph (Single Column)
|
| 841 |
st.subheader("Monthly Heating and Cooling Load")
|
| 842 |
+
monthly_df = pd.DataFrame(loads).groupby("month").agg({
|
| 843 |
+
"total_cooling": "sum",
|
| 844 |
+
"total_heating": "sum"
|
| 845 |
+
}).reset_index()
|
| 846 |
+
monthly_df = monthly_df.rename(columns={"total_cooling": "Cooling", "total_heating": "Heating"})
|
| 847 |
fig = px.bar(
|
| 848 |
+
monthly_df,
|
| 849 |
x="month",
|
| 850 |
y=["Cooling", "Heating"],
|
| 851 |
barmode="group",
|
|
|
|
| 854 |
fig.update_xaxes(title="Month", tickvals=list(range(1, 13)))
|
| 855 |
fig.update_yaxes(title="Load (kW)")
|
| 856 |
st.plotly_chart(fig, use_container_width=True)
|
| 857 |
+
st.session_state.project_data["hvac_loads"]["monthly_summary"] = monthly_df.to_dict()
|
| 858 |
|
| 859 |
# Third Row: Load Breakdown (Two Columns)
|
| 860 |
col3, col4 = st.columns(2)
|
| 861 |
with col3:
|
| 862 |
st.subheader("Cooling Load Breakdown")
|
| 863 |
+
cooling_breakdown = {
|
| 864 |
+
"Conduction": sum(load["conduction_cooling"] for load in cooling_loads),
|
| 865 |
+
"Solar Gains": sum(load["solar"] for load in cooling_loads),
|
| 866 |
+
"Internal": sum(load["internal"] for load in cooling_loads),
|
| 867 |
+
"Ventilation": sum(load["ventilation_cooling"] for load in cooling_loads),
|
| 868 |
+
"Infiltration": sum(load["infiltration_cooling"] for load in cooling_loads)
|
| 869 |
+
}
|
| 870 |
+
cooling_pie = make_pie({k: v for k, v in cooling_breakdown.items() if v > 0}, "Cooling Load Components")
|
| 871 |
st.plotly_chart(cooling_pie)
|
| 872 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_component"] = cooling_breakdown
|
| 873 |
|
| 874 |
with col4:
|
| 875 |
st.subheader("Heating Load Breakdown")
|
| 876 |
+
heating_breakdown = {
|
| 877 |
+
"Conduction": sum(load["conduction_heating"] for load in heating_loads),
|
| 878 |
+
"Ventilation": sum(load["ventilation_heating"] for load in heating_loads),
|
| 879 |
+
"Infiltration": sum(load["infiltration_heating"] for load in heating_loads)
|
| 880 |
+
}
|
| 881 |
+
heating_pie = make_pie({k: v for k, v in heating_breakdown.items() if v > 0}, "Heating Load Components")
|
| 882 |
st.plotly_chart(heating_pie)
|
| 883 |
+
st.session_state.project_data["hvac_loads"]["heating"]["charts"]["pie_by_component"] = heating_breakdown
|
| 884 |
|
| 885 |
# Fourth Row: Heat Gain by Orientation (Two Columns)
|
| 886 |
col5, col6 = st.columns(2)
|
| 887 |
with col5:
|
| 888 |
st.subheader("Cooling Heat Gain by Orientation")
|
| 889 |
+
orientation_solar = defaultdict(float)
|
| 890 |
+
orientation_conduction = defaultdict(float)
|
| 891 |
+
for load in cooling_loads:
|
| 892 |
+
for key, value in load["solar_by_orientation"].items():
|
| 893 |
+
orientation_solar[key] += value
|
| 894 |
+
for key, value in load["conduction_by_orientation"].items():
|
| 895 |
+
orientation_conduction[key] += value
|
| 896 |
+
orientation_breakdown = {k: orientation_solar[k] + orientation_conduction[k] for k in set(orientation_solar) | set(orientation_conduction)}
|
| 897 |
+
orientation_pie = make_pie({k: v for k, v in orientation_breakdown.items() if v > 0}, "Cooling Heat Gain by Orientation")
|
| 898 |
st.plotly_chart(orientation_pie)
|
| 899 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["charts"]["pie_by_orientation"] = orientation_breakdown
|
| 900 |
|
| 901 |
with col6:
|
| 902 |
st.subheader("Heating Heat Gain by Orientation")
|
| 903 |
+
orientation_conduction = defaultdict(float)
|
| 904 |
+
for load in heating_loads:
|
| 905 |
+
for key, value in load["conduction_by_orientation"].items():
|
| 906 |
+
orientation_conduction[key] += value
|
| 907 |
+
orientation_breakdown = {k: v for k, v in orientation_conduction.items() if v > 0}
|
| 908 |
+
orientation_pie = make_pie(orientation_breakdown, "Heating Heat Gain by Orientation")
|
| 909 |
st.plotly_chart(orientation_pie)
|
| 910 |
+
st.session_state.project_data["hvac_loads"]["heating"]["charts"]["pie_by_orientation"] = orientation_breakdown
|
| 911 |
|
| 912 |
# Fifth Row: Explore Hourly Loads (Single Column)
|
| 913 |
st.subheader("Explore Hourly Loads")
|
| 914 |
+
df = pd.DataFrame(loads)
|
| 915 |
+
# Flatten orientation-based loads
|
| 916 |
+
unique_orientations = set()
|
| 917 |
+
for load in loads:
|
| 918 |
+
unique_orientations.update(load["solar_by_orientation"].keys())
|
| 919 |
+
unique_orientations.update(load["conduction_by_orientation"].keys())
|
| 920 |
|
| 921 |
+
columns = [
|
| 922 |
+
"month", "day", "hour",
|
| 923 |
+
"total_cooling", "total_heating",
|
| 924 |
+
"conduction_cooling", "solar", "internal",
|
| 925 |
+
"ventilation_cooling", "infiltration_cooling",
|
| 926 |
+
"ventilation_heating", "infiltration_heating"
|
| 927 |
+
]
|
| 928 |
+
for orient in sorted(unique_orientations):
|
| 929 |
+
df[f"solar_{orient}"] = df["solar_by_orientation"].apply(lambda x: x.get(orient, 0.0))
|
| 930 |
+
df[f"conduction_{orient}"] = df["conduction_by_orientation"].apply(lambda x: x.get(orient, 0.0))
|
| 931 |
+
columns.extend([f"solar_{orient}", f"conduction_{orient}"])
|
| 932 |
+
|
| 933 |
+
st.dataframe(df[columns])
|
| 934 |
+
|
| 935 |
+
# CSV Export
|
| 936 |
+
csv = df[columns].to_csv(index=False)
|
| 937 |
+
st.download_button("Download Hourly Summary as CSV", data=csv, file_name="hourly_loads.csv")
|
| 938 |
|
| 939 |
def display_hvac_loads_page():
|
| 940 |
"""
|
|
|
|
| 946 |
st.markdown("Configure and calculate HVAC loads for the building.")
|
| 947 |
|
| 948 |
# Generate a unique run ID for this session
|
| 949 |
+
import uuid
|
| 950 |
run_id = str(uuid.uuid4())
|
| 951 |
|
| 952 |
# Check for existing results
|
|
|
|
| 957 |
heating_loads = st.session_state.project_data["hvac_loads"]["heating"].get("hourly", [])
|
| 958 |
loads.extend(cooling_loads)
|
| 959 |
loads.extend(heating_loads)
|
| 960 |
+
# Sort loads by month, day, hour to ensure consistent display
|
| 961 |
loads = sorted(loads, key=lambda x: (x["month"], x["day"], x["hour"]))
|
| 962 |
if loads:
|
| 963 |
st.info("Displaying previously calculated HVAC load results.")
|
|
|
|
| 973 |
"Latitude": climate_data.get("location", {}).get("latitude", 0.0),
|
| 974 |
"Longitude": climate_data.get("location", {}).get("longitude", 0.0),
|
| 975 |
"Elevation": climate_data.get("location", {}).get("elevation", 0.0),
|
| 976 |
+
"Time Zone": climate_data.get("location", {}).get("timezone", "UTC"),
|
| 977 |
"Ground Reflectivity": climate_data.get("ground_reflectivity", 0.2)
|
| 978 |
}
|
| 979 |
|
|
|
|
| 1200 |
st.session_state.project_data["hvac_loads"]["cooling"]["charts"] = {}
|
| 1201 |
st.session_state.project_data["hvac_loads"]["heating"]["charts"] = {}
|
| 1202 |
|
| 1203 |
+
# Store breakdown
|
| 1204 |
+
cooling_breakdown = {
|
| 1205 |
+
"conduction": sum(load["conduction_cooling"] for load in cooling_loads),
|
| 1206 |
+
"solar": sum(load["solar"] for load in cooling_loads if load["total_cooling"] > 0),
|
| 1207 |
+
"internal": sum(load["internal"] for load in cooling_loads if load["total_cooling"] > 0),
|
| 1208 |
+
"ventilation": sum(load["ventilation_cooling"] for load in cooling_loads),
|
| 1209 |
+
"infiltration": sum(load["infiltration_cooling"] for load in cooling_loads)
|
| 1210 |
+
}
|
| 1211 |
+
heating_breakdown = {
|
| 1212 |
+
"conduction": sum(load["conduction_heating"] for load in heating_loads),
|
| 1213 |
+
"ventilation": sum(load["ventilation_heating"] for load in heating_loads),
|
| 1214 |
+
"infiltration": sum(load["infiltration_heating"] for load in heating_loads)
|
| 1215 |
+
}
|
| 1216 |
+
st.session_state.project_data["hvac_loads"]["cooling"]["breakdown"] = cooling_breakdown
|
| 1217 |
+
st.session_state.project_data["hvac_loads"]["heating"]["breakdown"] = heating_breakdown
|
| 1218 |
|
| 1219 |
# Display Results UI with unique run_id
|
| 1220 |
display_hvac_results_ui(loads, run_id=run_id)
|