Spaces:
Sleeping
Sleeping
Update app/hvac_loads.py
Browse files- app/hvac_loads.py +73 -45
app/hvac_loads.py
CHANGED
@@ -22,6 +22,50 @@ from utils.ctf_calculations import CTFCalculator, ComponentType, CTFCoefficients
|
|
22 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
23 |
logger = logging.getLogger(__name__)
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
class TFMCalculations:
|
26 |
# Solar calculation constants (from utils/solar.py)
|
27 |
SHGC_COEFFICIENTS = {
|
@@ -632,7 +676,8 @@ class TFMCalculations:
|
|
632 |
|
633 |
@staticmethod
|
634 |
def get_adaptive_comfort_temp(outdoor_temp: float) -> float:
|
635 |
-
"""
|
|
|
636 |
if 10 <= outdoor_temp <= 33.5:
|
637 |
return 0.31 * outdoor_temp + 17.8
|
638 |
return 24.0
|
@@ -662,9 +707,9 @@ class TFMCalculations:
|
|
662 |
return filtered_data
|
663 |
|
664 |
@staticmethod
|
665 |
-
def get_indoor_conditions(indoor_conditions: Dict, hour: int, outdoor_temp: float) -> Dict:
|
666 |
"""Determine indoor conditions based on user settings."""
|
667 |
-
if indoor_conditions["type"] == "Fixed":
|
668 |
mode = "none" if abs(outdoor_temp - 18) < 0.01 else "cooling" if outdoor_temp > 18 else "heating"
|
669 |
if mode == "cooling":
|
670 |
return {
|
@@ -678,16 +723,10 @@ class TFMCalculations:
|
|
678 |
}
|
679 |
else:
|
680 |
return {"temperature": 24.0, "rh": 50.0}
|
681 |
-
elif indoor_conditions["type"] == "Time-varying":
|
682 |
-
schedule = indoor_conditions.get("schedule", [])
|
683 |
-
if schedule:
|
684 |
-
hour_idx = hour % 24
|
685 |
-
for entry in schedule:
|
686 |
-
if entry["hour"] == hour_idx:
|
687 |
-
return {"temperature": entry["temperature"], "rh": entry["rh"]}
|
688 |
-
return {"temperature": 24.0, "rh": 50.0}
|
689 |
else: # Adaptive
|
690 |
-
|
|
|
|
|
691 |
|
692 |
@staticmethod
|
693 |
def calculate_tfm_loads(components: Dict, hourly_data: List[Dict], indoor_conditions: Dict, internal_loads: Dict, building_info: Dict, sim_period: Dict, hvac_settings: Dict) -> List[Dict]:
|
@@ -703,6 +742,12 @@ class TFMCalculations:
|
|
703 |
st.session_state.material_library = MaterialLibrary()
|
704 |
logger.info("Initialized MaterialLibrary in session_state for solar calculations")
|
705 |
|
|
|
|
|
|
|
|
|
|
|
|
|
706 |
for comp_list in components.values():
|
707 |
for comp in comp_list:
|
708 |
comp['ctf'] = CTFCalculator.calculate_ctf_coefficients(comp)
|
@@ -711,7 +756,9 @@ class TFMCalculations:
|
|
711 |
for hour_data in filtered_data:
|
712 |
hour = hour_data["hour"]
|
713 |
outdoor_temp = hour_data["dry_bulb"]
|
714 |
-
|
|
|
|
|
715 |
indoor_temp = indoor_cond["temperature"]
|
716 |
conduction_cooling = conduction_heating = solar = internal = ventilation_cooling = ventilation_heating = infiltration_cooling = infiltration_heating = 0
|
717 |
is_operating = False
|
@@ -755,8 +802,8 @@ class TFMCalculations:
|
|
755 |
total_cooling = 0
|
756 |
temp_loads.append({
|
757 |
"hour": hour,
|
758 |
-
"month":
|
759 |
-
"day":
|
760 |
"conduction_cooling": conduction_cooling,
|
761 |
"conduction_heating": conduction_heating,
|
762 |
"solar": solar,
|
@@ -838,13 +885,13 @@ def display_hvac_loads_page():
|
|
838 |
st.subheader("Indoor Conditions")
|
839 |
indoor_type = st.selectbox(
|
840 |
"Indoor Conditions Type",
|
841 |
-
["Fixed
|
842 |
key="hvac_indoor_type",
|
843 |
-
index=["Fixed
|
844 |
)
|
845 |
st.session_state.project_data["indoor_conditions"]["type"] = indoor_type
|
846 |
|
847 |
-
if indoor_type == "Fixed":
|
848 |
col1, col2 = st.columns(2)
|
849 |
with col1:
|
850 |
cooling_temp = st.number_input(
|
@@ -888,33 +935,14 @@ def display_hvac_loads_page():
|
|
888 |
"temperature": heating_temp,
|
889 |
"rh": heating_rh
|
890 |
}
|
891 |
-
elif indoor_type == "
|
892 |
-
st.
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
temp = st.number_input(
|
900 |
-
f"Temperature (°C)",
|
901 |
-
min_value=16.0,
|
902 |
-
max_value=30.0,
|
903 |
-
value=24.0,
|
904 |
-
step=0.1,
|
905 |
-
key=f"hvac_schedule_temp_{hour}"
|
906 |
-
)
|
907 |
-
with col2:
|
908 |
-
rh = st.number_input(
|
909 |
-
f"Relative Humidity (%)",
|
910 |
-
min_value=30.0,
|
911 |
-
max_value=70.0,
|
912 |
-
value=50.0,
|
913 |
-
step=1.0,
|
914 |
-
key=f"hvac_schedule_rh_{hour}"
|
915 |
-
)
|
916 |
-
schedule.append({"hour": hour, "temperature": temp, "rh": rh})
|
917 |
-
st.session_state.project_data["indoor_conditions"]["schedule"] = schedule
|
918 |
|
919 |
# HVAC Settings Configuration
|
920 |
st.subheader("HVAC Settings")
|
|
|
22 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
23 |
logger = logging.getLogger(__name__)
|
24 |
|
25 |
+
class AdaptiveComfortModel:
|
26 |
+
@staticmethod
|
27 |
+
def compute_daily_mean_temperatures(hourly_data: List[Dict]) -> List[Tuple[Tuple[int, int], float]]:
|
28 |
+
daily_means = {}
|
29 |
+
for data in hourly_data:
|
30 |
+
key = (data["month"], data["day"])
|
31 |
+
daily_means.setdefault(key, []).append(data["dry_bulb"])
|
32 |
+
return [(key, np.mean(values)) for key, values in sorted(daily_means.items())]
|
33 |
+
|
34 |
+
@staticmethod
|
35 |
+
def compute_running_mean(daily_means: List[float], alpha: float = 0.8) -> List[float]:
|
36 |
+
trm = []
|
37 |
+
for i, t in enumerate(daily_means):
|
38 |
+
if i == 0:
|
39 |
+
trm.append(t)
|
40 |
+
else:
|
41 |
+
trm.append((1 - alpha) * t + alpha * trm[i - 1])
|
42 |
+
return trm
|
43 |
+
|
44 |
+
@staticmethod
|
45 |
+
def generate_adaptive_setpoints(hourly_data: List[Dict], acceptability: str = "90") -> Dict[Tuple[int, int], float]:
|
46 |
+
daily_mean_data = AdaptiveComfortModel.compute_daily_mean_temperatures(hourly_data)
|
47 |
+
daily_keys = [key for key, _ in daily_mean_data]
|
48 |
+
daily_values = [value for _, value in daily_mean_data]
|
49 |
+
running_means = AdaptiveComfortModel.compute_running_mean(daily_values)
|
50 |
+
|
51 |
+
setpoints = {}
|
52 |
+
for i, key in enumerate(daily_keys):
|
53 |
+
trm = running_means[i]
|
54 |
+
if acceptability == "80":
|
55 |
+
t_min = 0.31 * trm + 13.5
|
56 |
+
t_max = 0.31 * trm + 22.5
|
57 |
+
elif acceptability == "85":
|
58 |
+
t_min = 0.31 * trm + 13.9
|
59 |
+
t_max = 0.31 * trm + 22.1
|
60 |
+
elif acceptability == "95":
|
61 |
+
t_min = 0.31 * trm + 14.7
|
62 |
+
t_max = 0.31 * trm + 20.7
|
63 |
+
else: # Default to 90%
|
64 |
+
t_min = 0.31 * trm + 14.3
|
65 |
+
t_max = 0.31 * trm + 21.3
|
66 |
+
setpoints[key] = (t_min + t_max) / 2
|
67 |
+
return setpoints
|
68 |
+
|
69 |
class TFMCalculations:
|
70 |
# Solar calculation constants (from utils/solar.py)
|
71 |
SHGC_COEFFICIENTS = {
|
|
|
676 |
|
677 |
@staticmethod
|
678 |
def get_adaptive_comfort_temp(outdoor_temp: float) -> float:
|
679 |
+
"""Deprecated: Use AdaptiveComfortModel instead."""
|
680 |
+
logger.warning("get_adaptive_comfort_temp is deprecated. Use AdaptiveComfortModel.generate_adaptive_setpoints.")
|
681 |
if 10 <= outdoor_temp <= 33.5:
|
682 |
return 0.31 * outdoor_temp + 17.8
|
683 |
return 24.0
|
|
|
707 |
return filtered_data
|
708 |
|
709 |
@staticmethod
|
710 |
+
def get_indoor_conditions(indoor_conditions: Dict, hour: int, outdoor_temp: float, month: int = 1, day: int = 1, adaptive_setpoints: Optional[Dict[Tuple[int, int], float]] = None) -> Dict:
|
711 |
"""Determine indoor conditions based on user settings."""
|
712 |
+
if indoor_conditions["type"] == "Fixed Setpoints":
|
713 |
mode = "none" if abs(outdoor_temp - 18) < 0.01 else "cooling" if outdoor_temp > 18 else "heating"
|
714 |
if mode == "cooling":
|
715 |
return {
|
|
|
723 |
}
|
724 |
else:
|
725 |
return {"temperature": 24.0, "rh": 50.0}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
726 |
else: # Adaptive
|
727 |
+
key = (month, day)
|
728 |
+
temp = adaptive_setpoints.get(key, 24.0) if adaptive_setpoints else 24.0
|
729 |
+
return {"temperature": temp, "rh": 50.0}
|
730 |
|
731 |
@staticmethod
|
732 |
def calculate_tfm_loads(components: Dict, hourly_data: List[Dict], indoor_conditions: Dict, internal_loads: Dict, building_info: Dict, sim_period: Dict, hvac_settings: Dict) -> List[Dict]:
|
|
|
742 |
st.session_state.material_library = MaterialLibrary()
|
743 |
logger.info("Initialized MaterialLibrary in session_state for solar calculations")
|
744 |
|
745 |
+
if indoor_conditions["type"] == "Adaptive":
|
746 |
+
acceptability = indoor_conditions.get("adaptive_acceptability", "90")
|
747 |
+
adaptive_setpoints = AdaptiveComfortModel.generate_adaptive_setpoints(hourly_data, acceptability)
|
748 |
+
else:
|
749 |
+
adaptive_setpoints = None
|
750 |
+
|
751 |
for comp_list in components.values():
|
752 |
for comp in comp_list:
|
753 |
comp['ctf'] = CTFCalculator.calculate_ctf_coefficients(comp)
|
|
|
756 |
for hour_data in filtered_data:
|
757 |
hour = hour_data["hour"]
|
758 |
outdoor_temp = hour_data["dry_bulb"]
|
759 |
+
month = hour_data["month"]
|
760 |
+
day = hour_data["day"]
|
761 |
+
indoor_cond = TFMCalculations.get_indoor_conditions(indoor_conditions, hour, outdoor_temp, month, day, adaptive_setpoints)
|
762 |
indoor_temp = indoor_cond["temperature"]
|
763 |
conduction_cooling = conduction_heating = solar = internal = ventilation_cooling = ventilation_heating = infiltration_cooling = infiltration_heating = 0
|
764 |
is_operating = False
|
|
|
802 |
total_cooling = 0
|
803 |
temp_loads.append({
|
804 |
"hour": hour,
|
805 |
+
"month": month,
|
806 |
+
"day": day,
|
807 |
"conduction_cooling": conduction_cooling,
|
808 |
"conduction_heating": conduction_heating,
|
809 |
"solar": solar,
|
|
|
885 |
st.subheader("Indoor Conditions")
|
886 |
indoor_type = st.selectbox(
|
887 |
"Indoor Conditions Type",
|
888 |
+
["Fixed Setpoints", "Adaptive"],
|
889 |
key="hvac_indoor_type",
|
890 |
+
index=["Fixed Setpoints", "Adaptive"].index(st.session_state.project_data["indoor_conditions"]["type"])
|
891 |
)
|
892 |
st.session_state.project_data["indoor_conditions"]["type"] = indoor_type
|
893 |
|
894 |
+
if indoor_type == "Fixed Setpoints":
|
895 |
col1, col2 = st.columns(2)
|
896 |
with col1:
|
897 |
cooling_temp = st.number_input(
|
|
|
935 |
"temperature": heating_temp,
|
936 |
"rh": heating_rh
|
937 |
}
|
938 |
+
elif indoor_type == "Adaptive":
|
939 |
+
acceptability = st.selectbox(
|
940 |
+
"Adaptive Comfort Acceptability (%)",
|
941 |
+
["80", "85", "90", "95"],
|
942 |
+
index=["80", "85", "90", "95"].index(st.session_state.project_data["indoor_conditions"].get("adaptive_acceptability", "90")),
|
943 |
+
key="adaptive_acceptability"
|
944 |
+
)
|
945 |
+
st.session_state.project_data["indoor_conditions"]["adaptive_acceptability"] = acceptability
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
946 |
|
947 |
# HVAC Settings Configuration
|
948 |
st.subheader("HVAC Settings")
|