Update data/calculation.py
Browse files- data/calculation.py +27 -8
data/calculation.py
CHANGED
|
@@ -13,6 +13,7 @@ from data.material_library import Construction, GlazingMaterial, DoorMaterial
|
|
| 13 |
from data.internal_loads import PEOPLE_ACTIVITY_LEVELS, DIVERSITY_FACTORS, LIGHTING_FIXTURE_TYPES, EQUIPMENT_HEAT_GAINS, VENTILATION_RATES, INFILTRATION_SETTINGS
|
| 14 |
import scipy.linalg as linalg
|
| 15 |
from datetime import datetime
|
|
|
|
| 16 |
|
| 17 |
class ComponentType(Enum):
|
| 18 |
WALL = "Wall"
|
|
@@ -137,8 +138,6 @@ class TFMCalculations:
|
|
| 137 |
@staticmethod
|
| 138 |
def get_adaptive_comfort_temp(outdoor_temp: float) -> float:
|
| 139 |
"""Calculate adaptive comfort temperature per ASHRAE 55."""
|
| 140 |
-
# Simplified ASHRAE 55 adaptive model: T_comf = 0.31 * T_out + 17.8
|
| 141 |
-
# Valid for 10°C ≤ T_out ≤ 33.5°C
|
| 142 |
if 10 <= outdoor_temp <= 33.5:
|
| 143 |
return 0.31 * outdoor_temp + 17.8
|
| 144 |
return 24.0 # Default to standard setpoint if outside range
|
|
@@ -171,7 +170,6 @@ class TFMCalculations:
|
|
| 171 |
def get_indoor_conditions(indoor_conditions: Dict, hour: int, outdoor_temp: float) -> Dict:
|
| 172 |
"""Determine indoor conditions based on user settings."""
|
| 173 |
if indoor_conditions["type"] == "Fixed":
|
| 174 |
-
# Select cooling or heating setpoint based on outdoor temperature
|
| 175 |
mode = "none" if abs(outdoor_temp - 18) < 0.01 else "cooling" if outdoor_temp > 18 else "heating"
|
| 176 |
if mode == "cooling":
|
| 177 |
return {
|
|
@@ -183,7 +181,7 @@ class TFMCalculations:
|
|
| 183 |
"temperature": indoor_conditions.get("heating_setpoint", {}).get("temperature", 22.0),
|
| 184 |
"rh": indoor_conditions.get("heating_setpoint", {}).get("rh", 50.0)
|
| 185 |
}
|
| 186 |
-
else:
|
| 187 |
return {"temperature": 24.0, "rh": 50.0}
|
| 188 |
elif indoor_conditions["type"] == "Time-varying":
|
| 189 |
schedule = indoor_conditions.get("schedule", [])
|
|
@@ -200,7 +198,7 @@ class TFMCalculations:
|
|
| 200 |
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]:
|
| 201 |
"""Calculate TFM loads for heating and cooling with user-defined filters and temperature threshold."""
|
| 202 |
filtered_data = TFMCalculations.filter_hourly_data(hourly_data, sim_period, building_info)
|
| 203 |
-
|
| 204 |
building_orientation = building_info.get("orientation_angle", 0.0)
|
| 205 |
operating_periods = hvac_settings.get("operating_hours", [{"start": 8, "end": 18}])
|
| 206 |
area = building_info.get("floor_area", 100.0)
|
|
@@ -243,14 +241,15 @@ class TFMCalculations:
|
|
| 243 |
# Calculate total loads, subtracting internal load for heating
|
| 244 |
total_cooling = conduction_cooling + solar + internal + ventilation_cooling + infiltration_cooling
|
| 245 |
total_heating = max(conduction_heating + ventilation_heating + infiltration_heating - internal, 0)
|
| 246 |
-
# Enforce mutual exclusivity
|
| 247 |
if mode == "cooling":
|
| 248 |
total_heating = 0
|
| 249 |
elif mode == "heating":
|
| 250 |
total_cooling = 0
|
| 251 |
-
|
| 252 |
"hour": hour,
|
| 253 |
"month": hour_data["month"],
|
|
|
|
| 254 |
"conduction_cooling": conduction_cooling,
|
| 255 |
"conduction_heating": conduction_heating,
|
| 256 |
"solar": solar,
|
|
@@ -262,4 +261,24 @@ class TFMCalculations:
|
|
| 262 |
"total_cooling": total_cooling,
|
| 263 |
"total_heating": total_heating
|
| 264 |
})
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
from data.internal_loads import PEOPLE_ACTIVITY_LEVELS, DIVERSITY_FACTORS, LIGHTING_FIXTURE_TYPES, EQUIPMENT_HEAT_GAINS, VENTILATION_RATES, INFILTRATION_SETTINGS
|
| 14 |
import scipy.linalg as linalg
|
| 15 |
from datetime import datetime
|
| 16 |
+
from collections import defaultdict
|
| 17 |
|
| 18 |
class ComponentType(Enum):
|
| 19 |
WALL = "Wall"
|
|
|
|
| 138 |
@staticmethod
|
| 139 |
def get_adaptive_comfort_temp(outdoor_temp: float) -> float:
|
| 140 |
"""Calculate adaptive comfort temperature per ASHRAE 55."""
|
|
|
|
|
|
|
| 141 |
if 10 <= outdoor_temp <= 33.5:
|
| 142 |
return 0.31 * outdoor_temp + 17.8
|
| 143 |
return 24.0 # Default to standard setpoint if outside range
|
|
|
|
| 170 |
def get_indoor_conditions(indoor_conditions: Dict, hour: int, outdoor_temp: float) -> Dict:
|
| 171 |
"""Determine indoor conditions based on user settings."""
|
| 172 |
if indoor_conditions["type"] == "Fixed":
|
|
|
|
| 173 |
mode = "none" if abs(outdoor_temp - 18) < 0.01 else "cooling" if outdoor_temp > 18 else "heating"
|
| 174 |
if mode == "cooling":
|
| 175 |
return {
|
|
|
|
| 181 |
"temperature": indoor_conditions.get("heating_setpoint", {}).get("temperature", 22.0),
|
| 182 |
"rh": indoor_conditions.get("heating_setpoint", {}).get("rh", 50.0)
|
| 183 |
}
|
| 184 |
+
else:
|
| 185 |
return {"temperature": 24.0, "rh": 50.0}
|
| 186 |
elif indoor_conditions["type"] == "Time-varying":
|
| 187 |
schedule = indoor_conditions.get("schedule", [])
|
|
|
|
| 198 |
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]:
|
| 199 |
"""Calculate TFM loads for heating and cooling with user-defined filters and temperature threshold."""
|
| 200 |
filtered_data = TFMCalculations.filter_hourly_data(hourly_data, sim_period, building_info)
|
| 201 |
+
temp_loads = []
|
| 202 |
building_orientation = building_info.get("orientation_angle", 0.0)
|
| 203 |
operating_periods = hvac_settings.get("operating_hours", [{"start": 8, "end": 18}])
|
| 204 |
area = building_info.get("floor_area", 100.0)
|
|
|
|
| 241 |
# Calculate total loads, subtracting internal load for heating
|
| 242 |
total_cooling = conduction_cooling + solar + internal + ventilation_cooling + infiltration_cooling
|
| 243 |
total_heating = max(conduction_heating + ventilation_heating + infiltration_heating - internal, 0)
|
| 244 |
+
# Enforce mutual exclusivity within hour
|
| 245 |
if mode == "cooling":
|
| 246 |
total_heating = 0
|
| 247 |
elif mode == "heating":
|
| 248 |
total_cooling = 0
|
| 249 |
+
temp_loads.append({
|
| 250 |
"hour": hour,
|
| 251 |
"month": hour_data["month"],
|
| 252 |
+
"day": hour_data["day"],
|
| 253 |
"conduction_cooling": conduction_cooling,
|
| 254 |
"conduction_heating": conduction_heating,
|
| 255 |
"solar": solar,
|
|
|
|
| 261 |
"total_cooling": total_cooling,
|
| 262 |
"total_heating": total_heating
|
| 263 |
})
|
| 264 |
+
# Group loads by day and apply daily control
|
| 265 |
+
loads_by_day = defaultdict(list)
|
| 266 |
+
for load in temp_loads:
|
| 267 |
+
day_key = (load["month"], load["day"])
|
| 268 |
+
loads_by_day[day_key].append(load)
|
| 269 |
+
final_loads = []
|
| 270 |
+
for day_key, day_loads in loads_by_day.items():
|
| 271 |
+
# Count hours with non-zero cooling and heating loads
|
| 272 |
+
cooling_hours = sum(1 for load in day_loads if load["total_cooling"] > 0)
|
| 273 |
+
heating_hours = sum(1 for load in day_loads if load["total_heating"] > 0)
|
| 274 |
+
# Apply daily control
|
| 275 |
+
for load in day_loads:
|
| 276 |
+
if cooling_hours > heating_hours:
|
| 277 |
+
load["total_heating"] = 0 # Keep cooling components, zero heating total
|
| 278 |
+
elif heating_hours > cooling_hours:
|
| 279 |
+
load["total_cooling"] = 0 # Keep heating components, zero cooling total
|
| 280 |
+
else: # Equal hours
|
| 281 |
+
load["total_cooling"] = 0
|
| 282 |
+
load["total_heating"] = 0 # Zero both totals, keep components
|
| 283 |
+
final_loads.append(load)
|
| 284 |
+
return final_loads
|